Commit 60713be4 authored by Becky Zhou's avatar Becky Zhou Committed by Commit Bot

[EoC] Add overflow menu to the toolbar V1

Add overflow menu button and options to open settings and send feedback
on the toolbar. The button will be shown on bottom sheet opened and
hidden on sheet closed.

Bug: 831782
Change-Id: I37f9290b914f1adec06a3617e8c0a1fb54b2ccd3
Reviewed-on: https://chromium-review.googlesource.com/1022952
Commit-Queue: Becky Zhou <huayinz@chromium.org>
Reviewed-by: default avatarBernhard Bauer <bauerb@chromium.org>
Reviewed-by: default avatarTheresa <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#553661}
parent 89ad97cc
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
<org.chromium.chrome.browser.contextual_suggestions.ToolbarView <org.chromium.chrome.browser.contextual_suggestions.ToolbarView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:chrome="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" > android:orientation="vertical" >
...@@ -35,15 +37,22 @@ ...@@ -35,15 +37,22 @@
android:ellipsize="end" android:ellipsize="end"
android:textAppearance="@style/BlackTitle1" /> android:textAppearance="@style/BlackTitle1" />
<!-- Use 50dp width and 16dp end/start padding to produce an 18dp, centered icon. --> <org.chromium.chrome.browser.widget.ListMenuButton
android:id="@+id/more"
android:layout_width="@dimen/contextual_suggestions_icon_size"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"
android:src="@drawable/ic_more_vert_black_24dp"
chrome:chrometint="@color/dark_mode_tint"
tools:ignore="ContentDescription" />
<ImageView <ImageView
android:id="@+id/close_button" android:id="@+id/close_button"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_width="wrap_content" android:layout_width="@dimen/contextual_suggestions_icon_size"
android:background="?attr/selectableItemBackground"
android:src="@drawable/btn_close" android:src="@drawable/btn_close"
android:scaleType="center" android:scaleType="center"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:contentDescription="@string/close" /> android:contentDescription="@string/close" />
</LinearLayout> </LinearLayout>
......
...@@ -556,6 +556,9 @@ ...@@ -556,6 +556,9 @@
<!-- Padding surrounding the message. --> <!-- Padding surrounding the message. -->
<dimen name="reader_mode_infobar_text_padding">8dp</dimen> <dimen name="reader_mode_infobar_text_padding">8dp</dimen>
<!-- Contextual Suggestions Dimensions -->
<dimen name="contextual_suggestions_icon_size">48dp</dimen>
<!-- Defaults for TabbedModeFirstRunActivity values. --> <!-- Defaults for TabbedModeFirstRunActivity values. -->
<item type="dimen" name="dialog_fixed_width_major">100%</item> <item type="dimen" name="dialog_fixed_width_major">100%</item>
<item type="dimen" name="dialog_fixed_width_minor">100%</item> <item type="dimen" name="dialog_fixed_width_minor">100%</item>
......
...@@ -4,15 +4,21 @@ ...@@ -4,15 +4,21 @@
package org.chromium.chrome.browser.contextual_suggestions; package org.chromium.chrome.browser.contextual_suggestions;
import android.content.Intent;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import org.chromium.base.VisibleForTesting; import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.help.HelpAndFeedback;
import org.chromium.chrome.browser.preferences.ContextualSuggestionsPreference;
import org.chromium.chrome.browser.preferences.PreferencesLauncher;
import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.suggestions.SuggestionsNavigationDelegate; import org.chromium.chrome.browser.suggestions.SuggestionsNavigationDelegate;
import org.chromium.chrome.browser.suggestions.SuggestionsNavigationDelegateImpl; import org.chromium.chrome.browser.suggestions.SuggestionsNavigationDelegateImpl;
import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegateImpl; import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegateImpl;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.util.IntentUtils;
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; import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetObserver;
...@@ -167,6 +173,20 @@ public class ContextualSuggestionsCoordinator { ...@@ -167,6 +173,20 @@ public class ContextualSuggestionsCoordinator {
} }
} }
/** Show the settings page for contextual suggestions. */
void showSettings() {
Intent intent = PreferencesLauncher.createIntentForSettingsPage(
mActivity, ContextualSuggestionsPreference.class.getName());
IntentUtils.safeStartActivity(mActivity, intent);
}
/** Show the feedback page. */
void showFeedback() {
Tab currentTab = mActivity.getActivityTab();
HelpAndFeedback.getInstance(mActivity).showFeedback(
mActivity, mProfile, currentTab != null ? currentTab.getUrl() : null, null);
}
@VisibleForTesting @VisibleForTesting
ContextualSuggestionsMediator getMediatorForTesting() { ContextualSuggestionsMediator getMediatorForTesting() {
return mMediator; return mMediator;
......
...@@ -4,10 +4,12 @@ ...@@ -4,10 +4,12 @@
package org.chromium.chrome.browser.contextual_suggestions; package org.chromium.chrome.browser.contextual_suggestions;
import android.content.Context;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.View; import android.view.View;
import org.chromium.base.ContextUtils;
import org.chromium.base.VisibleForTesting; import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.feature_engagement.TrackerFactory; import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
...@@ -16,6 +18,7 @@ import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager.Fullscreen ...@@ -16,6 +18,7 @@ import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager.Fullscreen
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.ListMenuButton;
import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet.StateChangeReason; import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet.StateChangeReason;
import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetObserver; import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetObserver;
import org.chromium.chrome.browser.widget.bottomsheet.EmptyBottomSheetObserver; import org.chromium.chrome.browser.widget.bottomsheet.EmptyBottomSheetObserver;
...@@ -34,7 +37,8 @@ import java.util.List; ...@@ -34,7 +37,8 @@ import java.util.List;
* the contextual suggestions C++ components (via a bridge), updating the model, and communicating * the contextual suggestions C++ components (via a bridge), updating the model, and communicating
* with the component coordinator(s). * with the component coordinator(s).
*/ */
class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, FetchHelper.Delegate { class ContextualSuggestionsMediator
implements EnabledStateMonitor.Observer, FetchHelper.Delegate, ListMenuButton.Delegate {
private final Profile mProfile; private final Profile mProfile;
private final TabModelSelector mTabModelSelector; private final TabModelSelector mTabModelSelector;
private final ContextualSuggestionsCoordinator mCoordinator; private final ContextualSuggestionsCoordinator mCoordinator;
...@@ -52,6 +56,9 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -52,6 +56,9 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
private boolean mDidSuggestionsShowForTab; private boolean mDidSuggestionsShowForTab;
private boolean mHasRecordedPeekEventForTab; private boolean mHasRecordedPeekEventForTab;
/** Whether the content sheet is observed to be opened for the first time. */
private boolean mHasSheetBeenOpened;
/** /**
* Construct a new {@link ContextualSuggestionsMediator}. * Construct a new {@link ContextualSuggestionsMediator}.
* @param profile The regular {@link Profile}. * @param profile The regular {@link Profile}.
...@@ -191,6 +198,26 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -191,6 +198,26 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
} }
} }
// ListMenuButton.Delegate implementation.
@Override
public ListMenuButton.Item[] getItems() {
final Context context = ContextUtils.getApplicationContext();
return new ListMenuButton.Item[] {
new ListMenuButton.Item(context, R.string.menu_preferences, true),
new ListMenuButton.Item(context, R.string.menu_send_feedback, true)};
}
@Override
public void onItemSelected(ListMenuButton.Item item) {
if (item.getTextId() == R.string.menu_preferences) {
mCoordinator.showSettings();
} else if (item.getTextId() == R.string.menu_send_feedback) {
mCoordinator.showFeedback();
} else {
assert false : "Unhandled item detected.";
}
}
/** /**
* Called when suggestions are cleared either due to the user explicitly dismissing * Called when suggestions are cleared either due to the user explicitly dismissing
* suggestions via the close button or due to the FetchHelper signaling state should * suggestions via the close button or due to the FetchHelper signaling state should
...@@ -200,8 +227,11 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -200,8 +227,11 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
// TODO(twellington): Does this signal need to go back to FetchHelper? // TODO(twellington): Does this signal need to go back to FetchHelper?
mDidSuggestionsShowForTab = false; mDidSuggestionsShowForTab = false;
mHasRecordedPeekEventForTab = false; mHasRecordedPeekEventForTab = false;
mHasSheetBeenOpened = false;
mModel.setClusterList(new ClusterList(Collections.emptyList())); mModel.setClusterList(new ClusterList(Collections.emptyList()));
mModel.setCloseButtonOnClickListener(null); mModel.setCloseButtonOnClickListener(null);
mModel.setMenuButtonVisibility(false);
mModel.setMenuButtonDelegate(null);
mModel.setDefaultToolbarClickListener(null); mModel.setDefaultToolbarClickListener(null);
mModel.setTitle(null); mModel.setTitle(null);
mCoordinator.removeSuggestions(); mCoordinator.removeSuggestions();
...@@ -227,6 +257,8 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -227,6 +257,8 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
clearSuggestions(); clearSuggestions();
}); });
mModel.setMenuButtonVisibility(false);
mModel.setMenuButtonDelegate(this);
mModel.setDefaultToolbarClickListener(view -> mCoordinator.expandBottomSheet()); mModel.setDefaultToolbarClickListener(view -> mCoordinator.expandBottomSheet());
mModel.setTitle(title); mModel.setTitle(title);
mCoordinator.preloadContentInSheet(); mCoordinator.preloadContentInSheet();
...@@ -239,6 +271,7 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -239,6 +271,7 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
@Override @Override
public void onSheetFullyPeeked() { public void onSheetFullyPeeked() {
if (mHasRecordedPeekEventForTab) return; if (mHasRecordedPeekEventForTab) return;
assert !mHasSheetBeenOpened;
mHasRecordedPeekEventForTab = true; mHasRecordedPeekEventForTab = true;
TrackerFactory.getTrackerForProfile(mProfile).notifyEvent( TrackerFactory.getTrackerForProfile(mProfile).notifyEvent(
...@@ -255,14 +288,19 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -255,14 +288,19 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
@Override @Override
public void onSheetOpened(@StateChangeReason int reason) { public void onSheetOpened(@StateChangeReason int reason) {
TrackerFactory.getTrackerForProfile(mProfile).notifyEvent( if (!mHasSheetBeenOpened) {
EventConstants.CONTEXTUAL_SUGGESTIONS_OPENED); mHasSheetBeenOpened = true;
TrackerFactory.getTrackerForProfile(mProfile).notifyEvent(
mCoordinator.showSuggestions(mSuggestionsSource); EventConstants.CONTEXTUAL_SUGGESTIONS_OPENED);
mCoordinator.removeBottomSheetObserver(this); mCoordinator.showSuggestions(mSuggestionsSource);
mSheetObserver = null; reportEvent(ContextualSuggestionsEvent.UI_OPENED);
}
mModel.setMenuButtonVisibility(true);
}
reportEvent(ContextualSuggestionsEvent.UI_OPENED); @Override
public void onSheetClosed(int reason) {
mModel.setMenuButtonVisibility(false);
} }
}; };
......
...@@ -12,6 +12,7 @@ import org.chromium.chrome.browser.modelutil.PropertyObservable; ...@@ -12,6 +12,7 @@ import org.chromium.chrome.browser.modelutil.PropertyObservable;
import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder; import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder;
import org.chromium.chrome.browser.ntp.cards.NodeParent; import org.chromium.chrome.browser.ntp.cards.NodeParent;
import org.chromium.chrome.browser.ntp.cards.TreeNode; import org.chromium.chrome.browser.ntp.cards.TreeNode;
import org.chromium.chrome.browser.widget.ListMenuButton;
import java.util.Collections; import java.util.Collections;
...@@ -21,6 +22,8 @@ class ContextualSuggestionsModel ...@@ -21,6 +22,8 @@ class ContextualSuggestionsModel
/** Keys uniquely identifying model properties. */ /** Keys uniquely identifying model properties. */
static class PropertyKey { static class PropertyKey {
static final PropertyKey CLOSE_BUTTON_ON_CLICK_LISTENER = new PropertyKey(); static final PropertyKey CLOSE_BUTTON_ON_CLICK_LISTENER = new PropertyKey();
static final PropertyKey MENU_BUTTON_VISIBILITY = new PropertyKey();
static final PropertyKey MENU_BUTTON_DELEGATE = new PropertyKey();
static final PropertyKey TITLE = new PropertyKey(); static final PropertyKey TITLE = new PropertyKey();
static final PropertyKey TOOLBAR_SHADOW_VISIBILITY = new PropertyKey(); static final PropertyKey TOOLBAR_SHADOW_VISIBILITY = new PropertyKey();
static final PropertyKey DEFAULT_TOOLBAR_ON_CLICK_LISTENER = new PropertyKey(); static final PropertyKey DEFAULT_TOOLBAR_ON_CLICK_LISTENER = new PropertyKey();
...@@ -77,6 +80,8 @@ class ContextualSuggestionsModel ...@@ -77,6 +80,8 @@ class ContextualSuggestionsModel
ClusterListObservable mClusterListObservable = new ClusterListObservable(); ClusterListObservable mClusterListObservable = new ClusterListObservable();
private OnClickListener mCloseButtonOnClickListener; private OnClickListener mCloseButtonOnClickListener;
private boolean mMenuButtonVisibility;
private ListMenuButton.Delegate mMenuButtonDelegate;
private OnClickListener mDefaultToolbarOnClickListener; private OnClickListener mDefaultToolbarOnClickListener;
private String mTitle; private String mTitle;
private boolean mToolbarShadowVisibility; private boolean mToolbarShadowVisibility;
...@@ -102,6 +107,28 @@ class ContextualSuggestionsModel ...@@ -102,6 +107,28 @@ class ContextualSuggestionsModel
return mCloseButtonOnClickListener; return mCloseButtonOnClickListener;
} }
/** @param visible Whether the menu button is visible. */
void setMenuButtonVisibility(boolean visible) {
mMenuButtonVisibility = visible;
notifyPropertyChanged(PropertyKey.MENU_BUTTON_VISIBILITY);
}
/** @return Whether the menu button is visible. */
boolean getMenuButtonVisibility() {
return mMenuButtonVisibility;
}
/** @param delegate The delegate for handles actions for the menu. */
void setMenuButtonDelegate(ListMenuButton.Delegate delegate) {
mMenuButtonDelegate = delegate;
notifyPropertyChanged(PropertyKey.MENU_BUTTON_DELEGATE);
}
/** @return The delegate that handles actions for the menu. */
ListMenuButton.Delegate getMenuButtonDelegate() {
return mMenuButtonDelegate;
}
/** @param title The title to display in the toolbar. */ /** @param title The title to display in the toolbar. */
void setTitle(String title) { void setTitle(String title) {
mTitle = title; mTitle = title;
......
...@@ -42,6 +42,8 @@ class ToolbarCoordinator { ...@@ -42,6 +42,8 @@ class ToolbarCoordinator {
// The ToolbarCoordinator is created dynamically as needed, so the initial model state // The ToolbarCoordinator is created dynamically as needed, so the initial model state
// needs to be bound on creation. // needs to be bound on creation.
mModelChangeProcessor.onPropertyChanged(mModel, PropertyKey.CLOSE_BUTTON_ON_CLICK_LISTENER); mModelChangeProcessor.onPropertyChanged(mModel, PropertyKey.CLOSE_BUTTON_ON_CLICK_LISTENER);
mModelChangeProcessor.onPropertyChanged(mModel, PropertyKey.MENU_BUTTON_VISIBILITY);
mModelChangeProcessor.onPropertyChanged(mModel, PropertyKey.MENU_BUTTON_DELEGATE);
mModelChangeProcessor.onPropertyChanged(mModel, PropertyKey.TITLE); mModelChangeProcessor.onPropertyChanged(mModel, PropertyKey.TITLE);
mModelChangeProcessor.onPropertyChanged( mModelChangeProcessor.onPropertyChanged(
mModel, PropertyKey.DEFAULT_TOOLBAR_ON_CLICK_LISTENER); mModel, PropertyKey.DEFAULT_TOOLBAR_ON_CLICK_LISTENER);
......
...@@ -11,10 +11,12 @@ import android.widget.LinearLayout; ...@@ -11,10 +11,12 @@ import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.widget.ListMenuButton;
/** The toolbar view, containing an icon, title and close button. */ /** The toolbar view, containing an icon, title and close button. */
public class ToolbarView extends LinearLayout { public class ToolbarView extends LinearLayout {
private View mCloseButton; private View mCloseButton;
private ListMenuButton mMenuButton;
private TextView mTitle; private TextView mTitle;
private View mShadow; private View mShadow;
...@@ -27,6 +29,7 @@ public class ToolbarView extends LinearLayout { ...@@ -27,6 +29,7 @@ public class ToolbarView extends LinearLayout {
super.onFinishInflate(); super.onFinishInflate();
mCloseButton = findViewById(R.id.close_button); mCloseButton = findViewById(R.id.close_button);
mMenuButton = findViewById(R.id.more);
mTitle = (TextView) findViewById(R.id.title); mTitle = (TextView) findViewById(R.id.title);
mShadow = findViewById(R.id.shadow); mShadow = findViewById(R.id.shadow);
} }
...@@ -35,6 +38,14 @@ public class ToolbarView extends LinearLayout { ...@@ -35,6 +38,14 @@ public class ToolbarView extends LinearLayout {
mCloseButton.setOnClickListener(listener); mCloseButton.setOnClickListener(listener);
} }
void setMenuButtonDelegate(ListMenuButton.Delegate delegate) {
mMenuButton.setDelegate(delegate);
}
void setMenuButtonVisibility(boolean visible) {
mMenuButton.setVisibility(visible ? View.VISIBLE : View.GONE);
}
void setTitle(String title) { void setTitle(String title) {
mTitle.setText(title); mTitle.setText(title);
} }
......
...@@ -17,6 +17,10 @@ class ToolbarViewBinder ...@@ -17,6 +17,10 @@ class ToolbarViewBinder
public void bind(ContextualSuggestionsModel model, ToolbarView view, PropertyKey propertyKey) { public void bind(ContextualSuggestionsModel model, ToolbarView view, PropertyKey propertyKey) {
if (propertyKey == PropertyKey.CLOSE_BUTTON_ON_CLICK_LISTENER) { if (propertyKey == PropertyKey.CLOSE_BUTTON_ON_CLICK_LISTENER) {
view.setCloseButtonOnClickListener(model.getCloseButtonOnClickListener()); view.setCloseButtonOnClickListener(model.getCloseButtonOnClickListener());
} else if (propertyKey == PropertyKey.MENU_BUTTON_VISIBILITY) {
view.setMenuButtonVisibility(model.getMenuButtonVisibility());
} else if (propertyKey == PropertyKey.MENU_BUTTON_DELEGATE) {
view.setMenuButtonDelegate(model.getMenuButtonDelegate());
} else if (propertyKey == PropertyKey.TITLE) { } else if (propertyKey == PropertyKey.TITLE) {
view.setTitle(model.getTitle()); view.setTitle(model.getTitle());
} else if (propertyKey == PropertyKey.TOOLBAR_SHADOW_VISIBILITY) { } else if (propertyKey == PropertyKey.TOOLBAR_SHADOW_VISIBILITY) {
......
...@@ -2581,6 +2581,9 @@ Customize this anytime in <ph name="BEGIN_LINK1">&lt;LINK1&gt;</ph>Settings<ph n ...@@ -2581,6 +2581,9 @@ Customize this anytime in <ph name="BEGIN_LINK1">&lt;LINK1&gt;</ph>Settings<ph n
<message name="IDS_MENU_CLOSE_ALL_INCOGNITO_TABS" desc="Menu item for closing all open incognito tabs. [CHAR-LIMIT=27]"> <message name="IDS_MENU_CLOSE_ALL_INCOGNITO_TABS" desc="Menu item for closing all open incognito tabs. [CHAR-LIMIT=27]">
Close incognito tabs Close incognito tabs
</message> </message>
<message name="IDS_MENU_SEND_FEEDBACK" desc="Menu item for sending feedback. [CHAR-LIMIT=27]">
Send feedback
</message>
<!-- Bookmarks strings --> <!-- Bookmarks strings -->
<message name="IDS_BOOKMARKS" desc="Title of the bookmarks page, which shows a list of the user's bookmarks. [CHAR-LIMIT=18]"> <message name="IDS_BOOKMARKS" desc="Title of the bookmarks page, which shows a list of the user's bookmarks. [CHAR-LIMIT=18]">
......
...@@ -6,12 +6,14 @@ package org.chromium.chrome.browser.contextual_suggestions; ...@@ -6,12 +6,14 @@ package org.chromium.chrome.browser.contextual_suggestions;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import android.support.test.InstrumentationRegistry; import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest; import android.support.test.filters.MediumTest;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.View;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
...@@ -33,6 +35,7 @@ import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet; ...@@ -33,6 +35,7 @@ import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule; import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.util.ChromeTabUtils; import org.chromium.chrome.test.util.ChromeTabUtils;
import org.chromium.chrome.test.util.ViewUtils;
import org.chromium.chrome.test.util.browser.ChromeModernDesign; import org.chromium.chrome.test.util.browser.ChromeModernDesign;
import org.chromium.chrome.test.util.browser.Features; import org.chromium.chrome.test.util.browser.Features;
import org.chromium.chrome.test.util.browser.Features.EnableFeatures; import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
...@@ -255,6 +258,31 @@ public class ContextualSuggestionsTest { ...@@ -255,6 +258,31 @@ public class ContextualSuggestionsTest {
mBottomSheet.getSheetState()); mBottomSheet.getSheetState());
} }
@Test
@MediumTest
@Feature({"ContextualSuggestions"})
public void testMenuButton() throws Exception {
forceShowSuggestions();
View title =
mBottomSheet.getCurrentSheetContent().getToolbarView().findViewById(R.id.title);
View menuButton =
mBottomSheet.getCurrentSheetContent().getToolbarView().findViewById(R.id.more);
int titleWidth = title.getWidth();
// Menu button is not visible on peek state.
assertEquals(View.GONE, menuButton.getVisibility());
assertEquals(0, menuButton.getWidth());
// Menu button is visible after sheet is opened.
openSheet();
assertEquals(View.VISIBLE, menuButton.getVisibility());
// Title view should be resized.
ViewUtils.waitForStableView(menuButton);
assertNotEquals(0, menuButton.getWidth());
assertEquals(titleWidth, title.getWidth() + menuButton.getWidth());
}
private void forceShowSuggestions() throws InterruptedException, TimeoutException { private void forceShowSuggestions() throws InterruptedException, TimeoutException {
assertEquals("Model has incorrect number of items.", assertEquals("Model has incorrect number of items.",
(int) FakeContextualSuggestionsSource.TOTAL_ITEM_COUNT, (int) FakeContextualSuggestionsSource.TOTAL_ITEM_COUNT,
......
...@@ -113,4 +113,27 @@ public class ViewUtils { ...@@ -113,4 +113,27 @@ public class ViewUtils {
public static void waitForView(ViewGroup root, Matcher<View> viewMatcher) { public static void waitForView(ViewGroup root, Matcher<View> viewMatcher) {
waitForView(root, viewMatcher, VIEW_VISIBLE); waitForView(root, viewMatcher, VIEW_VISIBLE);
} }
/**
* Wait until the specified view has finished layout updates.
* @param view The specified view.
*/
public static void waitForStableView(final View view) {
CriteriaHelper.pollUiThread(new Criteria() {
@Override
public boolean isSatisfied() {
if (view.isDirty()) {
updateFailureReason("The view is dirty.");
return false;
}
if (view.isLayoutRequested()) {
updateFailureReason("The view has layout requested.");
return false;
}
return true;
}
});
}
} }
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