diff --git a/android/brave_java_resources.gni b/android/brave_java_resources.gni index e0498901b98..149610aa337 100644 --- a/android/brave_java_resources.gni +++ b/android/brave_java_resources.gni @@ -690,6 +690,11 @@ brave_java_resources = [ "java/res/layout/binance_spinner_dropdown_item.xml", "java/res/layout/binance_spinner_item.xml", "java/res/layout/binance_summary_item.xml", + "java/res/layout/bottom_toolbar.xml", + "java/res/layout/bottom_toolbar_browsing.xml", + "java/res/layout/bottom_toolbar_menu_button.xml", + "java/res/layout/bottom_toolbar_tab_switcher.xml", + "java/res/layout/icon_row_menu_footer.xml", "java/res/layout/brave_ads_existing_user_dialog_layout.xml", "java/res/layout/brave_ads_new_user_dialog_layout.xml", "java/res/layout/brave_bookmark_toolbar.xml", @@ -756,6 +761,7 @@ brave_java_resources = [ "java/res/layout/ntp_widget_list_item_layout.xml", "java/res/layout/qa_code_check.xml", "java/res/layout/report_broken_site_layout.xml", + "java/res/layout/toolbar_space.xml", "java/res/layout/top_sites_layout.xml", "java/res/layout/tracker_item_layout.xml", "java/res/layout/user_wallet_activity.xml", diff --git a/android/brave_java_sources.gni b/android/brave_java_sources.gni index 6916e16aff3..c62f0d291aa 100644 --- a/android/brave_java_sources.gni +++ b/android/brave_java_sources.gni @@ -147,11 +147,25 @@ brave_java_sources = [ "../../brave/android/java/org/chromium/chrome/browser/tabmodel/BraveTabCreator.java", "../../brave/android/java/org/chromium/chrome/browser/toolbar/BraveHomeButton.java", "../../brave/android/java/org/chromium/chrome/browser/toolbar/bottom/BookmarksButton.java", + "../../brave/android/java/org/chromium/chrome/browser/toolbar/bottom/BottomTabSwitcherActionMenuCoordinator.java", + "../../brave/android/java/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java", + "../../brave/android/java/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarNewTabButton.java", + "../../brave/android/java/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarVariationManager.java", "../../brave/android/java/org/chromium/chrome/browser/toolbar/bottom/BraveBottomToolbarConfiguration.java", "../../brave/android/java/org/chromium/chrome/browser/toolbar/bottom/BraveBottomToolbarCoordinator.java", - "../../brave/android/java/org/chromium/chrome/browser/toolbar/bottom/BraveBottomToolbarVariationManager.java", "../../brave/android/java/org/chromium/chrome/browser/toolbar/bottom/BraveBrowsingModeBottomToolbarCoordinator.java", "../../brave/android/java/org/chromium/chrome/browser/toolbar/bottom/BraveSearchAccelerator.java", + "../../brave/android/java/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java", + "../../brave/android/java/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarLinearLayout.java", + "../../brave/android/java/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarMediator.java", + "../../brave/android/java/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarModel.java", + "../../brave/android/java/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarViewBinder.java", + "../../brave/android/java/org/chromium/chrome/browser/toolbar/bottom/SearchAccelerator.java", + "../../brave/android/java/org/chromium/chrome/browser/toolbar/bottom/ShareButton.java", + "../../brave/android/java/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarCoordinator.java", + "../../brave/android/java/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarMediator.java", + "../../brave/android/java/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarModel.java", + "../../brave/android/java/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarViewBinder.java", "../../brave/android/java/org/chromium/chrome/browser/toolbar/top/BraveToolbarLayout.java", "../../brave/android/java/org/chromium/chrome/browser/ui/brave_tricks/checkbox_to_switch/CheckBoxPreference.java", "../../brave/android/java/org/chromium/chrome/browser/ui/messages/infobar/BraveSimpleConfirmInfoBarBuilder.java", diff --git a/android/java/apk_for_test.flags b/android/java/apk_for_test.flags index 2e8016f86b8..03b577a3329 100644 --- a/android/java/apk_for_test.flags +++ b/android/java/apk_for_test.flags @@ -73,7 +73,3 @@ -keep class org.chromium.base.CommandLineInitUtil { *** initCommandLine(...); } - --keep class org.chromium.chrome.browser.toolbar.bottom.BottomToolbarConfiguration { - *** isBottomToolbarEnabled(...); -} diff --git a/android/java/org/chromium/chrome/browser/appmenu/BraveTabbedAppMenuPropertiesDelegate.java b/android/java/org/chromium/chrome/browser/appmenu/BraveTabbedAppMenuPropertiesDelegate.java index e63e0acb8ed..35d0fd20220 100644 --- a/android/java/org/chromium/chrome/browser/appmenu/BraveTabbedAppMenuPropertiesDelegate.java +++ b/android/java/org/chromium/chrome/browser/appmenu/BraveTabbedAppMenuPropertiesDelegate.java @@ -28,7 +28,7 @@ import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tabbed_mode.TabbedAppMenuPropertiesDelegate; import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.toolbar.ToolbarManager; -import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarConfiguration; +import org.chromium.chrome.browser.toolbar.bottom.BraveBottomToolbarConfiguration; import org.chromium.chrome.browser.ui.appmenu.AppMenuDelegate; import org.chromium.chrome.browser.ui.appmenu.AppMenuHandler; @@ -114,7 +114,7 @@ public class BraveTabbedAppMenuPropertiesDelegate extends TabbedAppMenuPropertie // Hide bookmark button if bottom toolbar is enabled ImageButton bookmarkButton = view.findViewById(R.id.bookmark_this_page_id); - if (bookmarkButton != null && BottomToolbarConfiguration.isBottomToolbarEnabled()) { + if (bookmarkButton != null && BraveBottomToolbarConfiguration.isBottomToolbarEnabled()) { bookmarkButton.setVisibility(View.GONE); } diff --git a/android/java/org/chromium/chrome/browser/document/BraveLauncherActivity.java b/android/java/org/chromium/chrome/browser/document/BraveLauncherActivity.java index 94118905276..6108592d9c2 100644 --- a/android/java/org/chromium/chrome/browser/document/BraveLauncherActivity.java +++ b/android/java/org/chromium/chrome/browser/document/BraveLauncherActivity.java @@ -10,7 +10,7 @@ import android.os.Bundle; import org.chromium.chrome.browser.BraveHelper; import org.chromium.chrome.browser.preferences.SharedPreferencesManager; -import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarConfiguration; +import org.chromium.chrome.browser.toolbar.bottom.BraveBottomToolbarConfiguration; /** * Base class for ChromeLauncherActivity @@ -23,7 +23,7 @@ public class BraveLauncherActivity extends Activity { // Disable key checker to avoid asserts on Brave keys in debug SharedPreferencesManager.getInstance().disableKeyCheckerForTesting(); - BottomToolbarConfiguration.isBottomToolbarEnabled(); + BraveBottomToolbarConfiguration.isBottomToolbarEnabled(); BraveHelper.DisableFREDRP(); } } diff --git a/android/java/org/chromium/chrome/browser/settings/AppearancePreferences.java b/android/java/org/chromium/chrome/browser/settings/AppearancePreferences.java index 4e5ff7f7e21..50c8470a0d7 100644 --- a/android/java/org/chromium/chrome/browser/settings/AppearancePreferences.java +++ b/android/java/org/chromium/chrome/browser/settings/AppearancePreferences.java @@ -9,6 +9,7 @@ import static org.chromium.chrome.browser.settings.MainSettings.PREF_UI_THEME; import android.content.SharedPreferences; import android.os.Bundle; + import androidx.preference.Preference; import org.chromium.base.ContextUtils; @@ -20,10 +21,10 @@ import org.chromium.chrome.browser.BraveRewardsObserver; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.night_mode.NightModeUtils; import org.chromium.chrome.browser.preferences.BravePreferenceKeys; -import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarConfiguration; +import org.chromium.chrome.browser.settings.BravePreferenceFragment; +import org.chromium.chrome.browser.toolbar.bottom.BraveBottomToolbarConfiguration; import org.chromium.components.browser_ui.settings.ChromeSwitchPreference; import org.chromium.components.browser_ui.settings.SettingsUtils; -import org.chromium.chrome.browser.settings.BravePreferenceFragment; import org.chromium.ui.base.DeviceFormFactor; public class AppearancePreferences extends BravePreferenceFragment @@ -89,7 +90,7 @@ public class AppearancePreferences extends BravePreferenceFragment ContextUtils.getApplicationContext()); ((ChromeSwitchPreference) enableBottomToolbar) .setChecked(!isTablet - && BottomToolbarConfiguration.isBottomToolbarEnabled()); + && BraveBottomToolbarConfiguration.isBottomToolbarEnabled()); } } @@ -115,7 +116,7 @@ public class AppearancePreferences extends BravePreferenceFragment String key = preference.getKey(); if (BravePreferenceKeys.BRAVE_BOTTOM_TOOLBAR_ENABLED_KEY.equals(key)) { SharedPreferences prefs = ContextUtils.getAppSharedPreferences(); - Boolean originalStatus = BottomToolbarConfiguration.isBottomToolbarEnabled(); + Boolean originalStatus = BraveBottomToolbarConfiguration.isBottomToolbarEnabled(); prefs.edit() .putBoolean(BravePreferenceKeys.BRAVE_BOTTOM_TOOLBAR_ENABLED_KEY, !originalStatus) diff --git a/android/java/org/chromium/chrome/browser/toolbar/bottom/BookmarksButton.java b/android/java/org/chromium/chrome/browser/toolbar/bottom/BookmarksButton.java index 9d3cf1e1f3d..4268889e1fa 100644 --- a/android/java/org/chromium/chrome/browser/toolbar/bottom/BookmarksButton.java +++ b/android/java/org/chromium/chrome/browser/toolbar/bottom/BookmarksButton.java @@ -19,11 +19,12 @@ import org.chromium.chrome.browser.ThemeColorProvider.ThemeColorObserver; import org.chromium.chrome.browser.ThemeColorProvider.TintObserver; import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior; import org.chromium.chrome.browser.tab.Tab; +import org.chromium.ui.widget.ChromeImageButton; /** * The bookmarks button. */ -public class BookmarksButton extends ShareButton implements ThemeColorObserver, TintObserver { +public class BookmarksButton extends ChromeImageButton implements ThemeColorObserver, TintObserver { /** A provider that notifies components when the theme color changes.*/ private ThemeColorProvider mThemeColorProvider; private ColorStateList mCurrentTint; @@ -33,7 +34,6 @@ public class BookmarksButton extends ShareButton implements ThemeColorObserver, setImageDrawable(ContextCompat.getDrawable(context, R.drawable.btn_bookmark)); } - @Override public void destroy() { if (mThemeColorProvider != null) { mThemeColorProvider.removeThemeColorObserver(this); @@ -42,7 +42,6 @@ public class BookmarksButton extends ShareButton implements ThemeColorObserver, } } - @Override public void setThemeColorProvider(ThemeColorProvider themeColorProvider) { mThemeColorProvider = themeColorProvider; mThemeColorProvider.addThemeColorObserver(this); @@ -72,17 +71,10 @@ public class BookmarksButton extends ShareButton implements ThemeColorObserver, } setEnabled(editingAllowed); } - - @Override - public void setActivityTabProvider(ActivityTabProvider activityTabProvider) { - // sergz: Do nothing here, was added just to avoid extra patching - } - @Override public void setOverviewModeBehavior(OverviewModeBehavior overviewModeBehavior) { } - @Override public void updateButtonEnabledState(Tab tab) { } } diff --git a/android/java/org/chromium/chrome/browser/toolbar/bottom/BottomTabSwitcherActionMenuCoordinator.java b/android/java/org/chromium/chrome/browser/toolbar/bottom/BottomTabSwitcherActionMenuCoordinator.java new file mode 100644 index 00000000000..8f4ee18048d --- /dev/null +++ b/android/java/org/chromium/chrome/browser/toolbar/bottom/BottomTabSwitcherActionMenuCoordinator.java @@ -0,0 +1,51 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.toolbar.bottom; + +import android.view.View; +import android.view.View.OnLongClickListener; + +import org.chromium.base.Callback; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.toolbar.top.TabSwitcherActionMenuCoordinator; +import org.chromium.ui.modelutil.MVCListAdapter.ModelList; +import org.chromium.ui.widget.RectProvider; +import org.chromium.ui.widget.ViewRectProvider; + +/** + * The main coordinator for the Tab Switcher Action Menu on the bottom toolbar, + * responsible for creating the popup menu and building a list of menu items. + */ +public class BottomTabSwitcherActionMenuCoordinator extends TabSwitcherActionMenuCoordinator { + public static OnLongClickListener createOnLongClickListener(Callback onItemClicked) { + return createOnLongClickListener( + new BottomTabSwitcherActionMenuCoordinator(), onItemClicked); + } + + @Override + public ModelList buildMenuItems() { + ModelList itemList = new ModelList(); + itemList.add(buildListItemByMenuItemType(MenuItemType.NEW_TAB)); + itemList.add(buildListItemByMenuItemType(MenuItemType.NEW_INCOGNITO_TAB)); + itemList.add(buildListItemByMenuItemType(MenuItemType.DIVIDER)); + itemList.add(buildListItemByMenuItemType(MenuItemType.CLOSE_TAB)); + return itemList; + } + + @Override + protected RectProvider getRectProvider(View anchorView) { + ViewRectProvider rectProvider = new ViewRectProvider(anchorView); + rectProvider.setIncludePadding(true); + + // space between the icon and the border of the wrapper + int toolbarHeight = anchorView.getHeight(); + int iconHeight = + anchorView.getResources().getDimensionPixelSize(R.dimen.toolbar_icon_height); + int padding = (toolbarHeight - iconHeight) / 2; + rectProvider.setInsetPx(0, padding, padding / 2, 0); + + return rectProvider; + } +} diff --git a/android/java/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java b/android/java/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java new file mode 100644 index 00000000000..431f4ab37dd --- /dev/null +++ b/android/java/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java @@ -0,0 +1,262 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.toolbar.bottom; + +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnLongClickListener; +import android.view.ViewGroup; +import android.view.ViewStub; + +import org.chromium.base.Callback; +import org.chromium.base.metrics.RecordUserAction; +import org.chromium.base.supplier.ObservableSupplier; +import org.chromium.base.supplier.ObservableSupplierImpl; +import org.chromium.base.supplier.OneShotCallback; +import org.chromium.base.supplier.Supplier; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.ActivityTabProvider; +import org.chromium.chrome.browser.ThemeColorProvider; +import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver; +import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior; +import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior.OverviewModeObserver; +import org.chromium.chrome.browser.feature_engagement.TrackerFactory; +import org.chromium.chrome.browser.omnibox.LocationBar; +import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.share.ShareDelegate; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tasks.ReturnToChromeExperimentsUtil; +import org.chromium.chrome.browser.toolbar.IncognitoStateProvider; +import org.chromium.chrome.browser.toolbar.TabCountProvider; +import org.chromium.chrome.browser.ui.appmenu.AppMenuButtonHelper; +import org.chromium.components.feature_engagement.EventConstants; +import org.chromium.components.feature_engagement.Tracker; + +/** + * The root coordinator for the bottom toolbar. It has two sub-components: the browsing mode bottom + * toolbar and the tab switcher mode bottom toolbar. + */ +class BottomToolbarCoordinator { + /** The browsing mode bottom toolbar component */ + protected final BrowsingModeBottomToolbarCoordinator mBrowsingModeCoordinator; + + /** The tab switcher mode bottom toolbar component */ + private TabSwitcherBottomToolbarCoordinator mTabSwitcherModeCoordinator; + + /** The tab switcher mode bottom toolbar stub that will be inflated when native is ready. */ + private final ViewStub mTabSwitcherModeStub; + + /** A provider that notifies components when the theme color changes.*/ + private final ThemeColorProvider mThemeColorProvider; + + /** The overview mode manager. */ + protected OverviewModeBehavior mOverviewModeBehavior; + protected OverviewModeObserver mOverviewModeObserver; + + /** The activity tab provider. */ + private ActivityTabProvider mTabProvider; + + private final ObservableSupplier mShareDelegateSupplier; + private final Callback mShareDelegateSupplierCallback; + private ObservableSupplierImpl mShareButtonListenerSupplier = + new ObservableSupplierImpl<>(); + private final Supplier mShowStartSurfaceCallable; + private ObservableSupplier mOverviewModeBehaviorSupplier; + private Callback mOverviewModeBehaviorSupplierObserver; + private AppMenuButtonHelper mMenuButtonHelper; + + /** + * Build the coordinator that manages the bottom toolbar. + * @param stub The bottom toolbar {@link ViewStub} to inflate. + * @param tabProvider The {@link ActivityTabProvider} used for making the IPH. + * @param themeColorProvider The {@link ThemeColorProvider} for the bottom toolbar. + * @param shareDelegateSupplier The supplier for the {@link ShareDelegate} the bottom controls + * should use to share content. + * @param showStartSurfaceCallable The action that opens the start surface, returning true if + * the start surface is shown. + * @param openHomepageAction The action that opens the homepage. + * @param setUrlBarFocusAction The function that sets Url bar focus. The first argument is + * @param overviewModeBehaviorSupplier Supplier for the overview mode manager. + * @param menuButtonHelperSupplier + */ + BottomToolbarCoordinator(ViewStub stub, ActivityTabProvider tabProvider, + OnLongClickListener tabsSwitcherLongClickListner, ThemeColorProvider themeColorProvider, + ObservableSupplier shareDelegateSupplier, + Supplier showStartSurfaceCallable, Runnable openHomepageAction, + Callback setUrlBarFocusAction, + ObservableSupplier overviewModeBehaviorSupplier, + ObservableSupplier menuButtonHelperSupplier) { + View root = stub.inflate(); + + mOverviewModeBehaviorSupplierObserver = this::setOverviewModeBehavior; + mOverviewModeBehaviorSupplier = overviewModeBehaviorSupplier; + + mShowStartSurfaceCallable = showStartSurfaceCallable; + final OnClickListener homeButtonListener = v -> { + openHomepageAction.run(); + }; + + final OnClickListener searchAcceleratorListener = v -> { + RecordUserAction.record("MobileToolbarOmniboxAcceleratorTap"); + + // Only switch to HomePage when overview is showing. + if (mOverviewModeBehavior != null && mOverviewModeBehavior.overviewVisible()) { + mShowStartSurfaceCallable.get(); + } + setUrlBarFocusAction.onResult(LocationBar.OmniboxFocusReason.ACCELERATOR_TAP); + }; + + mBrowsingModeCoordinator = new BraveBrowsingModeBottomToolbarCoordinator(root, tabProvider, + homeButtonListener, searchAcceleratorListener, mShareButtonListenerSupplier, + tabsSwitcherLongClickListner, mOverviewModeBehaviorSupplier); + + mTabSwitcherModeStub = root.findViewById(R.id.bottom_toolbar_tab_switcher_mode_stub); + + mThemeColorProvider = themeColorProvider; + mTabProvider = tabProvider; + + mShareDelegateSupplier = shareDelegateSupplier; + mShareDelegateSupplierCallback = this::onShareDelegateAvailable; + mShareDelegateSupplier.addObserver(mShareDelegateSupplierCallback); + + new OneShotCallback<>(menuButtonHelperSupplier, (menuButtonHelper) -> { + if (menuButtonHelper != null) { + mMenuButtonHelper = menuButtonHelper; + } + }); + } + + /** + * Initialize the bottom toolbar with the components that had native initialization + * dependencies. + *

+ * Calling this must occur after the native library have completely loaded. + * @param tabSwitcherListener An {@link OnClickListener} that is triggered when the + * tab switcher button is clicked. + * @param newTabClickListener An {@link OnClickListener} that is triggered when the + * new tab button is clicked. + * @param tabCountProvider Updates the tab count number in the tab switcher button and in the + * incognito toggle tab layout. + * @param incognitoStateProvider Notifies components when incognito mode is entered or exited. + * @param topToolbarRoot The root {@link ViewGroup} of the top toolbar. + * @param closeAllTabsAction The runnable that closes all tabs in the current tab model. + */ + void initializeWithNative(OnClickListener tabSwitcherListener, + OnClickListener newTabClickListener, TabCountProvider tabCountProvider, + IncognitoStateProvider incognitoStateProvider, ViewGroup topToolbarRoot, + Runnable closeAllTabsAction) { + final OnClickListener closeTabsClickListener = v -> { + final boolean isIncognito = incognitoStateProvider.isIncognitoSelected(); + if (isIncognito) { + RecordUserAction.record("MobileToolbarCloseAllIncognitoTabsButtonTap"); + } else { + RecordUserAction.record("MobileToolbarCloseAllRegularTabsButtonTap"); + } + + closeAllTabsAction.run(); + }; + + + mBrowsingModeCoordinator.initializeWithNative(newTabClickListener, tabSwitcherListener, + mMenuButtonHelper, tabCountProvider, mThemeColorProvider, incognitoStateProvider); + mTabSwitcherModeCoordinator = new TabSwitcherBottomToolbarCoordinator(mTabSwitcherModeStub, + topToolbarRoot, incognitoStateProvider, mThemeColorProvider, newTabClickListener, + closeTabsClickListener, mMenuButtonHelper, tabCountProvider); + + // Do not change bottom bar if StartSurface Single Pane is enabled and HomePage is not + // customized. + if (!ReturnToChromeExperimentsUtil.shouldShowStartSurfaceAsTheHomePage() + && BottomToolbarVariationManager.shouldBottomToolbarBeVisibleInOverviewMode()) { + mOverviewModeObserver = new EmptyOverviewModeObserver() { + @Override + public void onOverviewModeStartedShowing(boolean showToolbar) { + mBrowsingModeCoordinator.getSearchAccelerator().setEnabled(false); + if (BottomToolbarVariationManager.isShareButtonOnBottom()) { + mBrowsingModeCoordinator.getShareButton().setEnabled(false); + } + if (BottomToolbarVariationManager.isHomeButtonOnBottom()) { + mBrowsingModeCoordinator.getHomeButton().setEnabled(false); + } + } + + @Override + public void onOverviewModeStartedHiding( + boolean showToolbar, boolean delayAnimation) { + mBrowsingModeCoordinator.getSearchAccelerator().setEnabled(true); + if (BottomToolbarVariationManager.isShareButtonOnBottom()) { + mBrowsingModeCoordinator.getShareButton().updateButtonEnabledState( + mTabProvider.get()); + } + if (BottomToolbarVariationManager.isHomeButtonOnBottom()) { + mBrowsingModeCoordinator.getHomeButton().updateButtonEnabledState( + mTabProvider.get()); + } + } + }; + mOverviewModeBehaviorSupplier.addObserver(mOverviewModeBehaviorSupplierObserver); + } + } + + /** + * @param isVisible Whether the bottom toolbar is visible. + */ + void setBottomToolbarVisible(boolean isVisible) { + if (mTabSwitcherModeCoordinator != null) { + mTabSwitcherModeCoordinator.showToolbarOnTop(!isVisible); + } + mBrowsingModeCoordinator.onVisibilityChanged(isVisible); + } + + /** + * Clean up any state when the bottom toolbar is destroyed. + */ + void destroy() { + mBrowsingModeCoordinator.destroy(); + if (mTabSwitcherModeCoordinator != null) { + mTabSwitcherModeCoordinator.destroy(); + mTabSwitcherModeCoordinator = null; + } + if (mOverviewModeBehavior != null) { + mOverviewModeBehavior.removeOverviewModeObserver(mOverviewModeObserver); + mOverviewModeBehavior = null; + } + if (mOverviewModeBehaviorSupplier != null) { + mOverviewModeBehaviorSupplier.removeObserver(mOverviewModeBehaviorSupplierObserver); + mOverviewModeBehaviorSupplier = null; + mOverviewModeBehaviorSupplierObserver = null; + } + mThemeColorProvider.destroy(); + mShareDelegateSupplier.removeObserver(mShareDelegateSupplierCallback); + } + + private void onShareDelegateAvailable(ShareDelegate shareDelegate) { + final OnClickListener shareButtonListener = v -> { + if (BottomToolbarVariationManager.isShareButtonOnBottom()) { + RecordUserAction.record("MobileBottomToolbarShareButton"); + } + + Tab tab = mTabProvider.get(); + shareDelegate.share(tab, /*shareDirectly=*/false); + }; + + mShareButtonListenerSupplier.set(shareButtonListener); + } + + private void setOverviewModeBehavior(OverviewModeBehavior overviewModeBehavior) { + assert overviewModeBehavior != null; + assert mOverviewModeBehavior + == null + : "TODO(https://crbug.com/1084528): the overview mode manager should set at most once."; + mOverviewModeBehavior = overviewModeBehavior; + mOverviewModeBehavior.addOverviewModeObserver(mOverviewModeObserver); + } + + public void updateBookmarkButton(boolean isBookmarked, boolean editingAllowed) { + if (mBrowsingModeCoordinator != null) { + ((BraveBrowsingModeBottomToolbarCoordinator) mBrowsingModeCoordinator) + .updateBookmarkButton(isBookmarked, editingAllowed); + } + } +} diff --git a/android/java/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarNewTabButton.java b/android/java/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarNewTabButton.java new file mode 100644 index 00000000000..a501280fc8a --- /dev/null +++ b/android/java/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarNewTabButton.java @@ -0,0 +1,112 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.toolbar.bottom; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; + +import androidx.annotation.StringRes; + +import org.chromium.base.ApiCompatibilityUtils; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.ThemeColorProvider; +import org.chromium.chrome.browser.ThemeColorProvider.ThemeColorObserver; +import org.chromium.chrome.browser.ThemeColorProvider.TintObserver; +import org.chromium.chrome.browser.toolbar.IncognitoStateProvider; +import org.chromium.chrome.browser.toolbar.IncognitoStateProvider.IncognitoStateObserver; +import org.chromium.chrome.browser.toolbar.ToolbarColors; +import org.chromium.ui.widget.ChromeImageButton; + +/** + * The tab switcher new tab button. + */ +class BottomToolbarNewTabButton extends ChromeImageButton + implements IncognitoStateObserver, ThemeColorObserver, TintObserver { + /** The gray pill background behind the plus icon. */ + private Drawable mBackground; + + /** The {@link Resources} used to compute the background color. */ + private final Resources mResources; + + /** A provider that notifies when incognito mode is entered or exited. */ + private IncognitoStateProvider mIncognitoStateProvider; + + /** A provider that notifies when the theme color changes.*/ + private ThemeColorProvider mThemeColorProvider; + + public BottomToolbarNewTabButton(Context context, AttributeSet attrs) { + super(context, attrs); + + mResources = context.getResources(); + } + + @Override + public void setBackground(Drawable background) { + super.setBackground(background); + mBackground = background; + } + + /** + * Clean up any state when the new tab button is destroyed. + */ + void destroy() { + if (mIncognitoStateProvider != null) { + mIncognitoStateProvider.removeObserver(this); + mIncognitoStateProvider = null; + } + if (mThemeColorProvider != null) { + mThemeColorProvider.removeThemeColorObserver(this); + mThemeColorProvider.removeTintObserver(this); + mThemeColorProvider = null; + } + } + + void setIncognitoStateProvider(IncognitoStateProvider incognitoStateProvider) { + mIncognitoStateProvider = incognitoStateProvider; + mIncognitoStateProvider.addIncognitoStateObserverAndTrigger(this); + } + + @Override + public void onIncognitoStateChanged(boolean isIncognito) { + @StringRes + int resId = isIncognito ? R.string.accessibility_toolbar_btn_new_incognito_tab + : R.string.accessibility_toolbar_btn_new_tab; + setContentDescription(getResources().getText(resId)); + updateBackground(); + } + + void setThemeColorProvider(ThemeColorProvider themeColorProvider) { + mThemeColorProvider = themeColorProvider; + mThemeColorProvider.addThemeColorObserver(this); + mThemeColorProvider.addTintObserver(this); + } + + @Override + public void onThemeColorChanged(int primaryColor, boolean shouldAnimate) { + updateBackground(); + } + + @Override + public void onTintChanged(ColorStateList tint, boolean useLight) { + ApiCompatibilityUtils.setImageTintList(this, tint); + updateBackground(); + } + + private void updateBackground() { + if (mThemeColorProvider == null || mIncognitoStateProvider == null || mBackground == null) { + return; + } + mBackground.setColorFilter( + ToolbarColors.getTextBoxColorForToolbarBackgroundInNonNativePage(mResources, + mThemeColorProvider.getThemeColor(), + mThemeColorProvider.useLight() + && mIncognitoStateProvider.isIncognitoSelected()), + PorterDuff.Mode.SRC_IN); + } +} diff --git a/android/java/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarVariationManager.java b/android/java/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarVariationManager.java new file mode 100644 index 00000000000..5445578d59a --- /dev/null +++ b/android/java/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarVariationManager.java @@ -0,0 +1,82 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.toolbar.bottom; + +import androidx.annotation.StringDef; +import androidx.annotation.VisibleForTesting; + +import org.chromium.chrome.browser.flags.CachedFeatureFlags; +import org.chromium.chrome.browser.flags.ChromeFeatureList; +import org.chromium.chrome.browser.flags.StringCachedFieldTrialParameter; +import org.chromium.chrome.browser.incognito.IncognitoUtils; +import org.chromium.chrome.browser.preferences.ChromePreferenceKeys; +import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities; +import org.chromium.chrome.features.start_surface.StartSurfaceConfiguration; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * The variation manager helps figure out the current variation and the visibility of buttons on + * bottom toolbar. Every operation related to the variation, e.g. getting variation value, should be + * through {@link BottomToolbarVariationManager} rather than calling {@link CachedFeatureFlags}. + */ +public class BottomToolbarVariationManager { + /** + * @return Whether or not share button should be visible on the top toolbar in portrait mode + * in the current variation. + */ + public static boolean isShareButtonOnBottom() { + return false; + } + + /** + * @return Whether or not new tab button should be visible on the bottom toolbar + * in portrait mode in the current variation. + */ + public static boolean isNewTabButtonOnBottom() { + return BraveBottomToolbarConfiguration.isBottomToolbarEnabled(); + } + + /** + * @return Whether or not menu button should be visible on the top toolbar + * in portrait mode in the current variation. + */ + public static boolean isMenuButtonOnBottom() { + return BraveBottomToolbarConfiguration.isBottomToolbarEnabled(); + } + + /** + * @return Whether or not bottom toolbar should be visible in overview mode of portrait mode + * in the current variation. + */ + public static boolean shouldBottomToolbarBeVisibleInOverviewMode() { + return BraveBottomToolbarConfiguration.isBottomToolbarEnabled(); + } + + /** + * @return Whether or not home button should be visible in top toolbar of portrait mode + * in current variation. + */ + public static boolean isHomeButtonOnBottom() { + return BraveBottomToolbarConfiguration.isBottomToolbarEnabled(); + } + + /** + * @return Whether or not tab switcher button should be visible in bottom toolbar + * of portrait mode in current variation. + */ + public static boolean isTabSwitcherOnBottom() { + return BraveBottomToolbarConfiguration.isBottomToolbarEnabled(); + } + + /** + * @return Whether or not bookmark button should be visible in bottom toolbar + * of portrait mode in current variation. + */ + public static boolean isBookmarkButtonOnBottom() { + return BraveBottomToolbarConfiguration.isBottomToolbarEnabled(); + } +} diff --git a/android/java/org/chromium/chrome/browser/toolbar/bottom/BraveBottomToolbarConfiguration.java b/android/java/org/chromium/chrome/browser/toolbar/bottom/BraveBottomToolbarConfiguration.java index 91940574c4e..5d33f455fbc 100644 --- a/android/java/org/chromium/chrome/browser/toolbar/bottom/BraveBottomToolbarConfiguration.java +++ b/android/java/org/chromium/chrome/browser/toolbar/bottom/BraveBottomToolbarConfiguration.java @@ -23,8 +23,8 @@ public class BraveBottomToolbarConfiguration { private static final int SMALL_SCREEN_HEIGHT = 640; public static boolean isBottomToolbarEnabled() { - if (DeviceFormFactor.isNonMultiDisplayContextOnTablet(ContextUtils.getApplicationContext()) - || TabUiFeatureUtilities.isTabGroupsAndroidEnabled()) { + if (DeviceFormFactor.isNonMultiDisplayContextOnTablet( + ContextUtils.getApplicationContext())) { return false; } SharedPreferences sharedPreferences = ContextUtils.getAppSharedPreferences(); diff --git a/android/java/org/chromium/chrome/browser/toolbar/bottom/BraveBottomToolbarCoordinator.java b/android/java/org/chromium/chrome/browser/toolbar/bottom/BraveBottomToolbarCoordinator.java index 9d51546118a..7a37226ddcb 100644 --- a/android/java/org/chromium/chrome/browser/toolbar/bottom/BraveBottomToolbarCoordinator.java +++ b/android/java/org/chromium/chrome/browser/toolbar/bottom/BraveBottomToolbarCoordinator.java @@ -29,9 +29,9 @@ import org.chromium.chrome.browser.share.ShareDelegate; import org.chromium.chrome.browser.toolbar.HomeButton; import org.chromium.chrome.browser.toolbar.IncognitoStateProvider; import org.chromium.chrome.browser.toolbar.TabCountProvider; +import org.chromium.chrome.browser.toolbar.bottom.BookmarksButton; import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarNewTabButton; import org.chromium.chrome.browser.toolbar.bottom.SearchAccelerator; -import org.chromium.chrome.browser.toolbar.bottom.ShareButton; import org.chromium.chrome.browser.ui.appmenu.AppMenuButtonHelper; import org.chromium.chrome.browser.util.TabUtils; import org.chromium.ui.widget.Toast; @@ -39,7 +39,7 @@ import org.chromium.ui.widget.Toast; public class BraveBottomToolbarCoordinator extends BottomToolbarCoordinator implements View.OnLongClickListener { private HomeButton mHomeButton; - private ShareButton mBookmarksButton; + private BookmarksButton mBookmarksButton; private SearchAccelerator mSearchAccelerator; private BottomToolbarNewTabButton mNewTabButton; private ActivityTabProvider mBraveTabProvider; @@ -150,7 +150,7 @@ public class BraveBottomToolbarCoordinator if (BottomToolbarVariationManager.isHomeButtonOnBottom()) { browsingModeCoordinator.getHomeButton().setVisibility(View.INVISIBLE); } - if (BraveBottomToolbarVariationManager.isBraveVariation()) { + if (BottomToolbarVariationManager.isBookmarkButtonOnBottom()) { browsingModeCoordinator.getBookmarkButton().setVisibility(View.INVISIBLE); } if (BottomToolbarVariationManager.isTabSwitcherOnBottom()) { @@ -177,7 +177,7 @@ public class BraveBottomToolbarCoordinator browsingModeCoordinator.getHomeButton().updateButtonEnabledState( mBraveTabProvider.get()); } - if (BraveBottomToolbarVariationManager.isBraveVariation()) { + if (BottomToolbarVariationManager.isBookmarkButtonOnBottom()) { browsingModeCoordinator.getBookmarkButton().setVisibility(View.VISIBLE); } if (BottomToolbarVariationManager.isTabSwitcherOnBottom()) { diff --git a/android/java/org/chromium/chrome/browser/toolbar/bottom/BraveBottomToolbarVariationManager.java b/android/java/org/chromium/chrome/browser/toolbar/bottom/BraveBottomToolbarVariationManager.java deleted file mode 100644 index 7b1191fa480..00000000000 --- a/android/java/org/chromium/chrome/browser/toolbar/bottom/BraveBottomToolbarVariationManager.java +++ /dev/null @@ -1,27 +0,0 @@ -/* Copyright (c) 2020 The Brave Authors. All rights reserved. - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.chromium.chrome.browser.toolbar.bottom; - -import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarConfiguration; - -/** - * Brave's extension for BottomToolbarVariationManager - */ -public class BraveBottomToolbarVariationManager - extends BottomToolbarVariationManager { - private static String sBraveVariation; - - private static @Variations String getBraveVariation() { - if (sBraveVariation != null) return sBraveVariation; - sBraveVariation = Variations.NONE; - return sBraveVariation; - } - - public static boolean isBraveVariation() { - return BottomToolbarConfiguration.isBottomToolbarEnabled() - && getBraveVariation().equals(Variations.NONE); - } -} diff --git a/android/java/org/chromium/chrome/browser/toolbar/bottom/BraveBrowsingModeBottomToolbarCoordinator.java b/android/java/org/chromium/chrome/browser/toolbar/bottom/BraveBrowsingModeBottomToolbarCoordinator.java index 711807ba59a..8f6af7808af 100644 --- a/android/java/org/chromium/chrome/browser/toolbar/bottom/BraveBrowsingModeBottomToolbarCoordinator.java +++ b/android/java/org/chromium/chrome/browser/toolbar/bottom/BraveBrowsingModeBottomToolbarCoordinator.java @@ -44,7 +44,7 @@ public class BraveBrowsingModeBottomToolbarCoordinator mBraveToolbarRoot = root.findViewById(R.id.bottom_toolbar_browsing); mBraveNewTabButton = mBraveToolbarRoot.findViewById(R.id.bottom_new_tab_button); mBookmarkButton = mBraveToolbarRoot.findViewById(R.id.bottom_bookmark_button); - if (BraveBottomToolbarVariationManager.isBraveVariation()) { + if (BottomToolbarVariationManager.isBookmarkButtonOnBottom()) { mBookmarkButton.setVisibility(View.VISIBLE); getNewTabButtonParent().setVisibility(View.GONE); OnClickListener bookmarkClickHandler = v -> { diff --git a/android/java/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java b/android/java/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java new file mode 100644 index 00000000000..2ffa1c03d94 --- /dev/null +++ b/android/java/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java @@ -0,0 +1,261 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.toolbar.bottom; + +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnLongClickListener; + +import org.chromium.base.Callback; +import org.chromium.base.supplier.ObservableSupplier; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.ActivityTabProvider; +import org.chromium.chrome.browser.ActivityTabProvider.HintlessActivityTabObserver; +import org.chromium.chrome.browser.ThemeColorProvider; +import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior; +import org.chromium.chrome.browser.feature_engagement.TrackerFactory; +import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tab.TabUtils; +import org.chromium.chrome.browser.tasks.ReturnToChromeExperimentsUtil; +import org.chromium.chrome.browser.toolbar.HomeButton; +import org.chromium.chrome.browser.toolbar.IncognitoStateProvider; +import org.chromium.chrome.browser.toolbar.TabCountProvider; +import org.chromium.chrome.browser.toolbar.TabSwitcherButtonCoordinator; +import org.chromium.chrome.browser.toolbar.TabSwitcherButtonView; +import org.chromium.chrome.browser.ui.appmenu.AppMenuButtonHelper; +import org.chromium.components.feature_engagement.FeatureConstants; +import org.chromium.components.feature_engagement.Tracker; +import org.chromium.ui.modelutil.PropertyModelChangeProcessor; + +/** + * The coordinator for the browsing mode bottom toolbar. This class has two primary components, + * an Android view that handles user actions and a composited texture that draws when the controls + * are being scrolled off-screen. The Android version does not draw unless the controls offset is 0. + */ +public class BrowsingModeBottomToolbarCoordinator { + /** The mediator that handles events from outside the browsing mode bottom toolbar. */ + private final BrowsingModeBottomToolbarMediator mMediator; + + /** The home button that lives in the bottom toolbar. */ + private final HomeButton mHomeButton; + + /** The share button that lives in the bottom toolbar. */ + private final ShareButton mShareButton; + + /** The new tab button that lives in the bottom toolbar. */ + private final BottomToolbarNewTabButton mNewTabButton; + + /** The search accelerator that lives in the bottom toolbar. */ + private final SearchAccelerator mSearchAccelerator; + + /** The tab switcher button component that lives in the bottom toolbar. */ + private final TabSwitcherButtonCoordinator mTabSwitcherButtonCoordinator; + + /** The tab switcher button view that lives in the bottom toolbar. */ + private final TabSwitcherButtonView mTabSwitcherButtonView; + + /** The view group that includes all views shown on browsing mode */ + private final BrowsingModeBottomToolbarLinearLayout mToolbarRoot; + + /** The model for the browsing mode bottom toolbar that holds all of its state. */ + private final BrowsingModeBottomToolbarModel mModel; + + /** The callback to be exectured when the share button on click listener is available. */ + private Callback mShareButtonListenerSupplierCallback; + + /** The supplier for the share button on click listener. */ + private ObservableSupplier mShareButtonListenerSupplier; + + /** The activity tab provider that used for making the IPH. */ + private final ActivityTabProvider mTabProvider; + + private Callback mOverviewModeBehaviorSupplierObserver; + private ObservableSupplier mOverviewModeBehaviorSupplier; + + /** + * Build the coordinator that manages the browsing mode bottom toolbar. + * @param root The root {@link View} for locating the views to inflate. + * @param tabProvider The {@link ActivityTabProvider} used for making the IPH. + * @param homeButtonListener The {@link OnClickListener} for the home button. + * @param searchAcceleratorListener The {@link OnClickListener} for the search accelerator. + * @param shareButtonListener The {@link OnClickListener} for the share button. + * @param overviewModeBehaviorSupplier Supplier for the overview mode manager. + */ + BrowsingModeBottomToolbarCoordinator(View root, ActivityTabProvider tabProvider, + OnClickListener homeButtonListener, OnClickListener searchAcceleratorListener, + ObservableSupplier shareButtonListenerSupplier, + OnLongClickListener tabSwitcherLongClickListener, + ObservableSupplier overviewModeBehaviorSupplier) { + mModel = new BrowsingModeBottomToolbarModel(); + mToolbarRoot = root.findViewById(R.id.bottom_toolbar_browsing); + mTabProvider = tabProvider; + + PropertyModelChangeProcessor.create( + mModel, mToolbarRoot, new BrowsingModeBottomToolbarViewBinder()); + + mMediator = new BrowsingModeBottomToolbarMediator(mModel); + + mHomeButton = mToolbarRoot.findViewById(R.id.bottom_home_button); + mHomeButton.setOnClickListener(homeButtonListener); + mHomeButton.setActivityTabProvider(mTabProvider); + + mNewTabButton = mToolbarRoot.findViewById(R.id.bottom_new_tab_button); + + mShareButton = mToolbarRoot.findViewById(R.id.bottom_share_button); + + mSearchAccelerator = mToolbarRoot.findViewById(R.id.search_accelerator); + mSearchAccelerator.setOnClickListener(searchAcceleratorListener); + + // TODO(amaralp): Make this adhere to MVC framework. + mTabSwitcherButtonView = mToolbarRoot.findViewById(R.id.bottom_tab_switcher_button); + mTabSwitcherButtonCoordinator = new TabSwitcherButtonCoordinator(mTabSwitcherButtonView); + + mTabSwitcherButtonView.setOnLongClickListener(tabSwitcherLongClickListener); + if (BottomToolbarVariationManager.isNewTabButtonOnBottom()) { + mNewTabButton.setVisibility(View.VISIBLE); + } + if (BottomToolbarVariationManager.isHomeButtonOnBottom()) { + mHomeButton.setVisibility(View.VISIBLE); + } + + if (BottomToolbarVariationManager.isTabSwitcherOnBottom()) { + mTabSwitcherButtonView.setVisibility(View.VISIBLE); + } + if (BottomToolbarVariationManager.isShareButtonOnBottom()) { + mShareButton.setVisibility(View.VISIBLE); + mShareButtonListenerSupplierCallback = shareButtonListener -> { + mShareButton.setOnClickListener(shareButtonListener); + }; + mShareButtonListenerSupplier = shareButtonListenerSupplier; + mShareButton.setActivityTabProvider(mTabProvider); + mShareButtonListenerSupplier.addObserver(mShareButtonListenerSupplierCallback); + } + + mOverviewModeBehaviorSupplier = overviewModeBehaviorSupplier; + mOverviewModeBehaviorSupplierObserver = this::setOverviewModeBehavior; + mOverviewModeBehaviorSupplier.addObserver(mOverviewModeBehaviorSupplierObserver); + } + + /** + * @param isVisible Whether the bottom toolbar is visible. + */ + void onVisibilityChanged(boolean isVisible) {} + + /** + * Initialize the bottom toolbar with the components that had native initialization + * dependencies. + *

+ * Calling this must occur after the native library have completely loaded. + * @param tabSwitcherListener An {@link OnClickListener} that is triggered when the + * tab switcher button is clicked. + * @param menuButtonHelper An {@link AppMenuButtonHelper} that is triggered when the + * menu button is clicked. + * @param tabCountProvider Updates the tab count number in the tab switcher button. + * @param themeColorProvider Notifies components when theme color changes. + * @param incognitoStateProvider Notifies components when incognito state changes. + */ + void initializeWithNative(OnClickListener newTabListener, OnClickListener tabSwitcherListener, + AppMenuButtonHelper menuButtonHelper, TabCountProvider tabCountProvider, + ThemeColorProvider themeColorProvider, IncognitoStateProvider incognitoStateProvider) { + mMediator.setThemeColorProvider(themeColorProvider); + if (BottomToolbarVariationManager.isNewTabButtonOnBottom()) { + mNewTabButton.setOnClickListener(newTabListener); + mNewTabButton.setThemeColorProvider(themeColorProvider); + mNewTabButton.setIncognitoStateProvider(incognitoStateProvider); + } + if (BottomToolbarVariationManager.isHomeButtonOnBottom()) { + mHomeButton.setThemeColorProvider(themeColorProvider); + } + + if (BottomToolbarVariationManager.isShareButtonOnBottom()) { + mShareButton.setThemeColorProvider(themeColorProvider); + } + + mSearchAccelerator.setThemeColorProvider(themeColorProvider); + mSearchAccelerator.setIncognitoStateProvider(incognitoStateProvider); + + if (BottomToolbarVariationManager.isTabSwitcherOnBottom()) { + mTabSwitcherButtonCoordinator.setTabSwitcherListener(tabSwitcherListener); + mTabSwitcherButtonCoordinator.setThemeColorProvider(themeColorProvider); + mTabSwitcherButtonCoordinator.setTabCountProvider(tabCountProvider); + } + } + + private void setOverviewModeBehavior(OverviewModeBehavior overviewModeBehavior) { + assert overviewModeBehavior != null; + + // If StartSurface is HomePage, BrowsingModeBottomToolbar is shown in browsing mode and in + // overview mode. We need to pass the OverviewModeBehavior to the buttons so they are + // disabled based on the overview state. + if (ReturnToChromeExperimentsUtil.shouldShowStartSurfaceAsTheHomePage()) { + mShareButton.setOverviewModeBehavior(overviewModeBehavior); + mTabSwitcherButtonCoordinator.setOverviewModeBehavior(overviewModeBehavior); + mHomeButton.setOverviewModeBehavior(overviewModeBehavior); + } + } + + /** + * @param enabled Whether to disable click events on the bottom toolbar. Setting true can also + * prevent from all click events on toolbar and all children views on toolbar. + */ + void setTouchEnabled(boolean enabled) { + mToolbarRoot.setTouchEnabled(enabled); + } + + /** + * @param visible Whether to hide the tab switcher bottom toolbar + */ + void setVisible(boolean visible) { + mModel.set(BrowsingModeBottomToolbarModel.IS_VISIBLE, visible); + } + + /** + * @return The browsing mode bottom toolbar's share button. + */ + ShareButton getShareButton() { + return mShareButton; + } + + /** + * @return The browsing mode bottom toolbar's tab switcher button. + */ + TabSwitcherButtonView getTabSwitcherButtonView() { + return mTabSwitcherButtonView; + } + + /** + * @return The browsing mode bottom toolbar's search button. + */ + SearchAccelerator getSearchAccelerator() { + return mSearchAccelerator; + } + + /** + * @return The browsing mode bottom toolbar's home button. + */ + HomeButton getHomeButton() { + return mHomeButton; + } + + /** + * Clean up any state when the browsing mode bottom toolbar is destroyed. + */ + public void destroy() { + if (mShareButtonListenerSupplier != null) { + mShareButtonListenerSupplier.removeObserver(mShareButtonListenerSupplierCallback); + } + if (mOverviewModeBehaviorSupplier != null) { + mOverviewModeBehaviorSupplier.removeObserver(mOverviewModeBehaviorSupplierObserver); + mOverviewModeBehaviorSupplier = null; + mOverviewModeBehaviorSupplierObserver = null; + } + mMediator.destroy(); + mHomeButton.destroy(); + mShareButton.destroy(); + mSearchAccelerator.destroy(); + mTabSwitcherButtonCoordinator.destroy(); + } +} diff --git a/android/java/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarLinearLayout.java b/android/java/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarLinearLayout.java new file mode 100644 index 00000000000..cf6fb2bd13f --- /dev/null +++ b/android/java/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarLinearLayout.java @@ -0,0 +1,52 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.toolbar.bottom; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.widget.LinearLayout; + +import androidx.annotation.Nullable; + +/** + * A linear layout which can intercept touch events to prevent from invoking click listeners on + * children views. + */ +public class BrowsingModeBottomToolbarLinearLayout extends LinearLayout { + private boolean mTouchEnabled = true; + + public BrowsingModeBottomToolbarLinearLayout(Context context) { + super(context); + } + + public BrowsingModeBottomToolbarLinearLayout(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public BrowsingModeBottomToolbarLinearLayout( + Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + /** + * @param enabled True if all touch events will be intercepted. + */ + public void setTouchEnabled(boolean enabled) { + mTouchEnabled = enabled; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (!mTouchEnabled) return true; + return super.onInterceptTouchEvent(ev); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (!mTouchEnabled) return true; + return super.onTouchEvent(event); + } +} diff --git a/android/java/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarMediator.java b/android/java/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarMediator.java new file mode 100644 index 00000000000..3daa61ac1e5 --- /dev/null +++ b/android/java/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarMediator.java @@ -0,0 +1,91 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.toolbar.bottom; + +import android.app.Activity; +import android.graphics.Color; +import android.view.View; + +import androidx.annotation.ColorInt; +import androidx.annotation.StringRes; +import androidx.appcompat.app.AppCompatActivity; + +import org.chromium.base.ApiCompatibilityUtils; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.AppHooks; +import org.chromium.chrome.browser.ThemeColorProvider; +import org.chromium.chrome.browser.ThemeColorProvider.ThemeColorObserver; +import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior; +import org.chromium.chrome.browser.flags.ChromeFeatureList; +import org.chromium.components.browser_ui.widget.FeatureHighlightProvider; +import org.chromium.components.feature_engagement.FeatureConstants; +import org.chromium.components.feature_engagement.Tracker; + +/** + * This class is responsible for reacting to events from the outside world, interacting with other + * coordinators, running most of the business logic associated with the browsing mode bottom + * toolbar, and updating the model accordingly. + */ +class BrowsingModeBottomToolbarMediator implements ThemeColorObserver { + /** The transparency fraction of the IPH bubble. */ + private static final float DUET_IPH_BUBBLE_ALPHA_FRACTION = 0.9f; + + /** The transparency fraction of the IPH background. */ + private static final float DUET_IPH_BACKGROUND_ALPHA_FRACTION = 0.3f; + + /** The dismissable parameter name of the IPH. */ + static final String DUET_IPH_TAP_TO_DISMISS_PARAM_NAME = "duet_iph_tap_to_dismiss_enabled"; + + /** The model for the browsing mode bottom toolbar that holds all of its state. */ + private final BrowsingModeBottomToolbarModel mModel; + + /** The overview mode manager. */ + private OverviewModeBehavior mOverviewModeBehavior; + + /** A provider that notifies components when the theme color changes.*/ + private ThemeColorProvider mThemeColorProvider; + + private FeatureHighlightProvider mFeatureHighlightProvider; + + /** + * Build a new mediator that handles events from outside the bottom toolbar. + * @param model The {@link BrowsingModeBottomToolbarModel} that holds all the state for the + * browsing mode bottom toolbar. + */ + BrowsingModeBottomToolbarMediator(BrowsingModeBottomToolbarModel model) { + mModel = model; + mFeatureHighlightProvider = AppHooks.get().createFeatureHighlightProvider(); + } + + void setThemeColorProvider(ThemeColorProvider themeColorProvider) { + mThemeColorProvider = themeColorProvider; + mThemeColorProvider.addThemeColorObserver(this); + } + + /** + * Clean up anything that needs to be when the bottom toolbar is destroyed. + */ + void destroy() { + if (mThemeColorProvider != null) { + mThemeColorProvider.removeThemeColorObserver(this); + mThemeColorProvider = null; + } + } + + @Override + public void onThemeColorChanged(int primaryColor, boolean shouldAnimate) { + mModel.set(BrowsingModeBottomToolbarModel.PRIMARY_COLOR, primaryColor); + } + + /** + * Set the alpha for the color. + * @param baseColor The color which alpha will apply to. + * @param alpha The desired alpha for the color. The value should between 0 to 1. 0 means total + * transparency, 1 means total non-transparency. + */ + private @ColorInt int applyCustomAlphaToColor(@ColorInt int baseColor, float alpha) { + return (baseColor & 0x00FFFFFF) | ((int) (alpha * 255) << 24); + } +} diff --git a/android/java/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarModel.java b/android/java/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarModel.java new file mode 100644 index 00000000000..0d866623f65 --- /dev/null +++ b/android/java/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarModel.java @@ -0,0 +1,25 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.toolbar.bottom; + +import org.chromium.ui.modelutil.PropertyModel; + +/** + * All of the state for the bottom toolbar, updated by the {@link + * BrowsingModeBottomToolbarCoordinator}. + */ +public class BrowsingModeBottomToolbarModel extends PropertyModel { + /** Primary color of bottom toolbar. */ + static final WritableIntPropertyKey PRIMARY_COLOR = new WritableIntPropertyKey(); + + /** Whether the browsing mode bottom toolbar is visible */ + static final WritableBooleanPropertyKey IS_VISIBLE = new WritableBooleanPropertyKey(); + + /** Default constructor. */ + BrowsingModeBottomToolbarModel() { + super(IS_VISIBLE, PRIMARY_COLOR); + set(IS_VISIBLE, true); + } +} diff --git a/android/java/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarViewBinder.java b/android/java/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarViewBinder.java new file mode 100644 index 00000000000..74c1f61d0cc --- /dev/null +++ b/android/java/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarViewBinder.java @@ -0,0 +1,38 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.toolbar.bottom; + +import android.view.View; + +import org.chromium.ui.modelutil.PropertyKey; +import org.chromium.ui.modelutil.PropertyModelChangeProcessor; + +/** + * This class is responsible for pushing updates to both the Android view and the compositor + * component of the browsing mode bottom toolbar. These updates are pulled from the + * {@link BrowsingModeBottomToolbarModel} when a notification of an update is received. + */ +public class BrowsingModeBottomToolbarViewBinder + implements PropertyModelChangeProcessor + .ViewBinder { + /** + * Build a binder that handles interaction between the model and the views that make up the + * browsing mode bottom toolbar. + */ + BrowsingModeBottomToolbarViewBinder() {} + + @Override + public final void bind( + BrowsingModeBottomToolbarModel model, View view, PropertyKey propertyKey) { + if (BrowsingModeBottomToolbarModel.PRIMARY_COLOR == propertyKey) { + view.setBackgroundColor(model.get(BrowsingModeBottomToolbarModel.PRIMARY_COLOR)); + } else if (BrowsingModeBottomToolbarModel.IS_VISIBLE == propertyKey) { + view.setVisibility(model.get(BrowsingModeBottomToolbarModel.IS_VISIBLE) ? View.VISIBLE + : View.GONE); + } else { + assert false : "Unhandled property detected in BrowsingModeBottomToolbarViewBinder!"; + } + } +} diff --git a/android/java/org/chromium/chrome/browser/toolbar/bottom/SearchAccelerator.java b/android/java/org/chromium/chrome/browser/toolbar/bottom/SearchAccelerator.java new file mode 100644 index 00000000000..e4bacfa1192 --- /dev/null +++ b/android/java/org/chromium/chrome/browser/toolbar/bottom/SearchAccelerator.java @@ -0,0 +1,100 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.toolbar.bottom; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; + +import org.chromium.base.ApiCompatibilityUtils; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.ThemeColorProvider; +import org.chromium.chrome.browser.ThemeColorProvider.ThemeColorObserver; +import org.chromium.chrome.browser.ThemeColorProvider.TintObserver; +import org.chromium.chrome.browser.toolbar.IncognitoStateProvider; +import org.chromium.chrome.browser.toolbar.IncognitoStateProvider.IncognitoStateObserver; +import org.chromium.chrome.browser.toolbar.ToolbarColors; +import org.chromium.ui.widget.ChromeImageButton; + +/** + * The search accelerator. + */ +class SearchAccelerator extends ChromeImageButton + implements ThemeColorObserver, TintObserver, IncognitoStateObserver { + /** The gray pill background behind the search icon. */ + private final Drawable mBackground; + + /** The {@link Resources} used to compute the background color. */ + private final Resources mResources; + + /** A provider that notifies components when the theme color changes.*/ + private ThemeColorProvider mThemeColorProvider; + + /** A provider that notifies when incognito mode is entered or exited. */ + private IncognitoStateProvider mIncognitoStateProvider; + + public SearchAccelerator(Context context, AttributeSet attrs) { + super(context, attrs); + + mResources = context.getResources(); + + mBackground = ApiCompatibilityUtils.getDrawable(mResources, R.drawable.ntp_search_box); + mBackground.mutate(); + setBackground(mBackground); + } + + void setThemeColorProvider(ThemeColorProvider themeColorProvider) { + mThemeColorProvider = themeColorProvider; + mThemeColorProvider.addThemeColorObserver(this); + mThemeColorProvider.addTintObserver(this); + } + + void setIncognitoStateProvider(IncognitoStateProvider provider) { + mIncognitoStateProvider = provider; + mIncognitoStateProvider.addIncognitoStateObserverAndTrigger(this); + } + + void destroy() { + if (mThemeColorProvider != null) { + mThemeColorProvider.removeThemeColorObserver(this); + mThemeColorProvider.removeTintObserver(this); + mThemeColorProvider = null; + } + + if (mIncognitoStateProvider != null) { + mIncognitoStateProvider.removeObserver(this); + mIncognitoStateProvider = null; + } + } + + @Override + public void onThemeColorChanged(int color, boolean shouldAnimate) { + updateBackground(); + } + + @Override + public void onTintChanged(ColorStateList tint, boolean useLight) { + ApiCompatibilityUtils.setImageTintList(this, tint); + updateBackground(); + } + + @Override + public void onIncognitoStateChanged(boolean isIncognito) { + updateBackground(); + } + + private void updateBackground() { + if (mThemeColorProvider == null || mIncognitoStateProvider == null) return; + + mBackground.setColorFilter(ToolbarColors.getTextBoxColorForToolbarBackgroundInNonNativePage( + mResources, mThemeColorProvider.getThemeColor(), + mIncognitoStateProvider.isIncognitoSelected() + && mThemeColorProvider.useLight()), + PorterDuff.Mode.SRC_IN); + } +} diff --git a/android/java/org/chromium/chrome/browser/toolbar/bottom/ShareButton.java b/android/java/org/chromium/chrome/browser/toolbar/bottom/ShareButton.java new file mode 100644 index 00000000000..b54faf1afa0 --- /dev/null +++ b/android/java/org/chromium/chrome/browser/toolbar/bottom/ShareButton.java @@ -0,0 +1,104 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.toolbar.bottom; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.util.AttributeSet; + +import org.chromium.base.ApiCompatibilityUtils; +import org.chromium.chrome.browser.ActivityTabProvider; +import org.chromium.chrome.browser.ActivityTabProvider.ActivityTabTabObserver; +import org.chromium.chrome.browser.ThemeColorProvider; +import org.chromium.chrome.browser.ThemeColorProvider.TintObserver; +import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver; +import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior; +import org.chromium.chrome.browser.share.ShareUtils; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.ui.widget.ChromeImageButton; + +/** + * The share button. + */ +class ShareButton extends ChromeImageButton implements TintObserver { + /** A provider that notifies components when the theme color changes.*/ + private ThemeColorProvider mThemeColorProvider; + + /** The {@link ActivityTabTabObserver} used to know when the active page changed. */ + private ActivityTabTabObserver mActivityTabTabObserver; + + /** The {@link OverviewModeBehavior} used to observe overview state changes. */ + private OverviewModeBehavior mOverviewModeBehavior; + + /** The {@link OvervieModeObserver} observing the OverviewModeBehavior */ + private OverviewModeBehavior.OverviewModeObserver mOverviewModeObserver; + + /** A collection of sharing utility functions.*/ + private ShareUtils mShareUtils; + + public ShareButton(Context context, AttributeSet attrs) { + super(context, attrs); + + mOverviewModeObserver = new EmptyOverviewModeObserver() { + @Override + public void onOverviewModeStartedShowing(boolean showTabSwitcherToolbar) { + setEnabled(false); + } + }; + + mShareUtils = new ShareUtils(); + } + + void setThemeColorProvider(ThemeColorProvider themeColorProvider) { + mThemeColorProvider = themeColorProvider; + mThemeColorProvider.addTintObserver(this); + } + + void setActivityTabProvider(ActivityTabProvider activityTabProvider) { + mActivityTabTabObserver = new ActivityTabTabObserver(activityTabProvider) { + @Override + public void onObservingDifferentTab(Tab tab, boolean hint) { + updateButtonEnabledState(tab); + } + + @Override + public void onUpdateUrl(Tab tab, String url) { + updateButtonEnabledState(tab); + } + }; + } + + public void setOverviewModeBehavior(OverviewModeBehavior overviewModeBehavior) { + assert overviewModeBehavior != null; + mOverviewModeBehavior = overviewModeBehavior; + mOverviewModeBehavior.addOverviewModeObserver(mOverviewModeObserver); + } + + void destroy() { + if (mThemeColorProvider != null) { + mThemeColorProvider.removeTintObserver(this); + mThemeColorProvider = null; + } + if (mActivityTabTabObserver != null) { + mActivityTabTabObserver.destroy(); + mActivityTabTabObserver = null; + } + + if (mOverviewModeBehavior != null) { + mOverviewModeBehavior.removeOverviewModeObserver(mOverviewModeObserver); + mOverviewModeObserver = null; + } + } + + public void updateButtonEnabledState(Tab tab) { + final boolean isEnabled = mShareUtils.shouldEnableShare(tab); + setEnabled(isEnabled); + } + + @Override + public void onTintChanged(ColorStateList tint, boolean useLight) { + ApiCompatibilityUtils.setImageTintList(this, tint); + } +} diff --git a/android/java/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarCoordinator.java b/android/java/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarCoordinator.java new file mode 100644 index 00000000000..28aaa3ba8f7 --- /dev/null +++ b/android/java/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarCoordinator.java @@ -0,0 +1,111 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.toolbar.bottom; + +import android.graphics.drawable.Drawable; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.view.ViewStub; + +import org.chromium.base.ApiCompatibilityUtils; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.ThemeColorProvider; +import org.chromium.chrome.browser.toolbar.IncognitoStateProvider; +import org.chromium.chrome.browser.toolbar.TabCountProvider; +import org.chromium.chrome.browser.toolbar.menu_button.MenuButton; +import org.chromium.chrome.browser.ui.appmenu.AppMenuButtonHelper; +import org.chromium.ui.modelutil.PropertyModelChangeProcessor; + +/** + * The coordinator for the tab switcher mode bottom toolbar. This class handles all interactions + * that the tab switcher bottom toolbar has with the outside world. + * TODO(crbug.com/1036474): This coordinator is not used currently and can be removed if the final + * duet design doesn't need a stand-alone toolbar in tab switcher mode. + */ +public class TabSwitcherBottomToolbarCoordinator { + /** The mediator that handles events from outside the tab switcher bottom toolbar. */ + private final TabSwitcherBottomToolbarMediator mMediator; + + /** The new tab button that lives in the tab switcher bottom toolbar. */ + private final BottomToolbarNewTabButton mNewTabButton; + + /** The menu button that lives in the tab switcher bottom toolbar. */ + private final MenuButton mMenuButton; + + /** The model for the tab switcher bottom toolbar that holds all of its state. */ + private final TabSwitcherBottomToolbarModel mModel; + + /** + * Build the coordinator that manages the tab switcher bottom toolbar. + * @param stub The tab switcher bottom toolbar {@link ViewStub} to inflate. + * @param topToolbarRoot The root {@link ViewGroup} of the top toolbar. + * @param incognitoStateProvider Notifies components when incognito mode is entered or exited. + * @param themeColorProvider Notifies components when the theme color changes. + * @param newTabClickListener An {@link OnClickListener} that is triggered when the + * new tab button is clicked. + * @param closeTabsClickListener An {@link OnClickListener} that is triggered when the + * close all tabs button is clicked. + * @param menuButtonHelper An {@link AppMenuButtonHelper} that is triggered when the + * menu button is clicked. + * @param tabCountProvider Updates the tab count number in the tab switcher button and in the + * incognito toggle tab layout. + */ + TabSwitcherBottomToolbarCoordinator(ViewStub stub, ViewGroup topToolbarRoot, + IncognitoStateProvider incognitoStateProvider, ThemeColorProvider themeColorProvider, + OnClickListener newTabClickListener, OnClickListener closeTabsClickListener, + AppMenuButtonHelper menuButtonHelper, TabCountProvider tabCountProvider) { + final ViewGroup root = (ViewGroup) stub.inflate(); + + View toolbar = root.findViewById(R.id.bottom_toolbar_buttons); + ViewGroup.LayoutParams params = toolbar.getLayoutParams(); + params.height = root.getResources().getDimensionPixelOffset(R.dimen.bottom_controls_height); + + mModel = new TabSwitcherBottomToolbarModel(); + + PropertyModelChangeProcessor.create(mModel, root, + new TabSwitcherBottomToolbarViewBinder( + topToolbarRoot, (ViewGroup) root.getParent())); + + mMediator = new TabSwitcherBottomToolbarMediator(mModel, themeColorProvider); + + mNewTabButton = root.findViewById(R.id.tab_switcher_new_tab_button); + Drawable background = + ApiCompatibilityUtils.getDrawable(root.getResources(), R.drawable.ntp_search_box); + background.mutate(); + mNewTabButton.setBackground(background); + mNewTabButton.setOnClickListener(newTabClickListener); + mNewTabButton.setIncognitoStateProvider(incognitoStateProvider); + mNewTabButton.setThemeColorProvider(themeColorProvider); + + assert menuButtonHelper != null; + mMenuButton = root.findViewById(R.id.menu_button_wrapper); + mMenuButton.setThemeColorProvider(themeColorProvider); + mMenuButton.setAppMenuButtonHelper(menuButtonHelper); + } + + /** + * @param showOnTop Whether to show the tab switcher bottom toolbar on the top of the screen. + */ + void showToolbarOnTop(boolean showOnTop) { + mMediator.showToolbarOnTop(showOnTop); + } + + /** + * @param visible Whether to hide the tab switcher bottom toolbar + */ + void setVisible(boolean visible) { + mModel.set(TabSwitcherBottomToolbarModel.IS_VISIBLE, visible); + } + + /** + * Clean up any state when the bottom toolbar is destroyed. + */ + public void destroy() { + mMediator.destroy(); + mNewTabButton.destroy(); + mMenuButton.destroy(); + } +} diff --git a/android/java/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarMediator.java b/android/java/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarMediator.java new file mode 100644 index 00000000000..fdbbd0e497c --- /dev/null +++ b/android/java/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarMediator.java @@ -0,0 +1,60 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.toolbar.bottom; + +import org.chromium.chrome.browser.ThemeColorProvider; +import org.chromium.chrome.browser.ThemeColorProvider.ThemeColorObserver; +import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities; + +/** + * This class is responsible for reacting to events from the outside world, interacting with other + * coordinators, running most of the business logic associated with the tab switcher bottom toolbar, + * and updating the model accordingly. + */ +class TabSwitcherBottomToolbarMediator implements ThemeColorObserver { + /** The model for the tab switcher bottom toolbar that holds all of its state. */ + private final TabSwitcherBottomToolbarModel mModel; + + /** A provider that notifies components when the theme color changes.*/ + private final ThemeColorProvider mThemeColorProvider; + + /** + * Build a new mediator that handles events from outside the tab switcher bottom toolbar. + * @param model The {@link TabSwitcherBottomToolbarModel} that holds all the state for the + * tab switcher bottom toolbar. + * @param themeColorProvider Notifies components when the theme color changes. + */ + TabSwitcherBottomToolbarMediator( + TabSwitcherBottomToolbarModel model, ThemeColorProvider themeColorProvider) { + mModel = model; + + mThemeColorProvider = themeColorProvider; + mThemeColorProvider.addThemeColorObserver(this); + } + + /** + * @param showOnTop Whether to show the tab switcher bottom toolbar on the top of the screen. + */ + void showToolbarOnTop(boolean showOnTop) { + // TODO(crbug.com/1012014): Resolve how to manage the toolbar position in tab switcher in + // landscape mode. Probably remove code about showing bottom toolbar on top. + // When GridTabSwitcher is enabled, show the original top toolbar instead of showing the + // bottom toolbar on top. + mModel.set(TabSwitcherBottomToolbarModel.SHOW_ON_TOP, + showOnTop && !TabUiFeatureUtilities.isGridTabSwitcherEnabled()); + } + + /** + * Clean up anything that needs to be when the tab switcher bottom toolbar is destroyed. + */ + void destroy() { + mThemeColorProvider.removeThemeColorObserver(this); + } + + @Override + public void onThemeColorChanged(int primaryColor, boolean shouldAnimate) { + mModel.set(TabSwitcherBottomToolbarModel.PRIMARY_COLOR, primaryColor); + } +} diff --git a/android/java/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarModel.java b/android/java/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarModel.java new file mode 100644 index 00000000000..03a43a3314c --- /dev/null +++ b/android/java/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarModel.java @@ -0,0 +1,27 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.toolbar.bottom; + +import org.chromium.ui.modelutil.PropertyModel; + +/** + * All of the state for the tab switcher bottom toolbar, updated by the + * {@link TabSwitcherBottomToolbarCoordinator}. + */ +public class TabSwitcherBottomToolbarModel extends PropertyModel { + /** Primary color of tab switcher bottom toolbar. */ + public static final WritableIntPropertyKey PRIMARY_COLOR = new WritableIntPropertyKey(); + + /** Whether the tab switcher bottom toolbar is visible */ + public static final WritableBooleanPropertyKey IS_VISIBLE = new WritableBooleanPropertyKey(); + + /** Whether the tab switcher bottom toolbar shows on top of the screen. */ + public static final WritableBooleanPropertyKey SHOW_ON_TOP = new WritableBooleanPropertyKey(); + + /** Default constructor. */ + public TabSwitcherBottomToolbarModel() { + super(PRIMARY_COLOR, IS_VISIBLE, SHOW_ON_TOP); + } +} diff --git a/android/java/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarViewBinder.java b/android/java/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarViewBinder.java new file mode 100644 index 00000000000..c262a5386e1 --- /dev/null +++ b/android/java/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarViewBinder.java @@ -0,0 +1,63 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.toolbar.bottom; + +import android.view.View; +import android.view.ViewGroup; + +import org.chromium.chrome.R; +import org.chromium.ui.UiUtils; +import org.chromium.ui.modelutil.PropertyKey; +import org.chromium.ui.modelutil.PropertyModelChangeProcessor; + +/** + * This class is responsible for pushing updates the view of the tab switcher bottom toolbar. These + * updates are pulled from the {@link TabSwitcherBottomToolbarModel} when a notification of an + * update is received. + */ +public class TabSwitcherBottomToolbarViewBinder + implements PropertyModelChangeProcessor + .ViewBinder { + private final ViewGroup mTopRoot; + private final ViewGroup mBottomRoot; + + /** + * Build a binder that handles interaction between the model and the tab switcher bottom toolbar + * view. + */ + TabSwitcherBottomToolbarViewBinder(ViewGroup topRoot, ViewGroup bottomRoot) { + mTopRoot = topRoot; + mBottomRoot = bottomRoot; + } + + @Override + public final void bind( + TabSwitcherBottomToolbarModel model, View view, PropertyKey propertyKey) { + if (TabSwitcherBottomToolbarModel.IS_VISIBLE == propertyKey) { + view.setVisibility( + model.get(TabSwitcherBottomToolbarModel.IS_VISIBLE) ? View.VISIBLE : View.GONE); + } else if (TabSwitcherBottomToolbarModel.PRIMARY_COLOR == propertyKey) { + view.findViewById(R.id.bottom_toolbar_buttons) + .setBackgroundColor(model.get(TabSwitcherBottomToolbarModel.PRIMARY_COLOR)); + } else if (TabSwitcherBottomToolbarModel.SHOW_ON_TOP == propertyKey) { + final boolean showOnTop = model.get(TabSwitcherBottomToolbarModel.SHOW_ON_TOP); + view.findViewById(R.id.bottom_toolbar_bottom_shadow) + .setVisibility(showOnTop ? View.VISIBLE : View.GONE); + // When shown on the bottom, the layout should match_parent so that it fills its + // parent container. When the layout is shown on the top, it should wrap_content + // so that the toolbar shadow is visible. + view.getLayoutParams().height = showOnTop ? ViewGroup.LayoutParams.WRAP_CONTENT + : ViewGroup.LayoutParams.MATCH_PARENT; + reparentView(view, showOnTop ? mTopRoot : mBottomRoot); + } else { + assert false : "Unhandled property detected in TabSwitcherBottomToolbarViewBinder!"; + } + } + + private static void reparentView(View v, ViewGroup newParent) { + UiUtils.removeViewFromParent(v); + newParent.addView(v); + } +} diff --git a/android/java/org/chromium/chrome/browser/toolbar/top/BraveToolbarLayout.java b/android/java/org/chromium/chrome/browser/toolbar/top/BraveToolbarLayout.java index b15e7b82e4b..962263a717b 100644 --- a/android/java/org/chromium/chrome/browser/toolbar/top/BraveToolbarLayout.java +++ b/android/java/org/chromium/chrome/browser/toolbar/top/BraveToolbarLayout.java @@ -770,7 +770,6 @@ public abstract class BraveToolbarLayout extends ToolbarLayout implements OnClic } } - @Override public void onBottomToolbarVisibilityChanged(boolean isVisible) { if (this instanceof ToolbarPhone && super.getMenuButtonCoordinator() != null) { super.getMenuButtonCoordinator().setVisibility(isVisible ? View.GONE : View.VISIBLE); diff --git a/android/java/res/layout/bottom_toolbar.xml b/android/java/res/layout/bottom_toolbar.xml new file mode 100644 index 00000000000..df816b89e62 --- /dev/null +++ b/android/java/res/layout/bottom_toolbar.xml @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/android/java/res/layout/bottom_toolbar_browsing.xml b/android/java/res/layout/bottom_toolbar_browsing.xml new file mode 100644 index 00000000000..97b06f32859 --- /dev/null +++ b/android/java/res/layout/bottom_toolbar_browsing.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/java/res/layout/bottom_toolbar_menu_button.xml b/android/java/res/layout/bottom_toolbar_menu_button.xml new file mode 100644 index 00000000000..a4badfa2358 --- /dev/null +++ b/android/java/res/layout/bottom_toolbar_menu_button.xml @@ -0,0 +1,36 @@ + + + + + + + + + + diff --git a/android/java/res/layout/bottom_toolbar_tab_switcher.xml b/android/java/res/layout/bottom_toolbar_tab_switcher.xml new file mode 100644 index 00000000000..63c4994f160 --- /dev/null +++ b/android/java/res/layout/bottom_toolbar_tab_switcher.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/java/res/layout/icon_row_menu_footer.xml b/android/java/res/layout/icon_row_menu_footer.xml new file mode 100644 index 00000000000..2382b933ff0 --- /dev/null +++ b/android/java/res/layout/icon_row_menu_footer.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/android/java/res/layout/toolbar_space.xml b/android/java/res/layout/toolbar_space.xml new file mode 100644 index 00000000000..b16e283817e --- /dev/null +++ b/android/java/res/layout/toolbar_space.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/android/java/res/values/brave_dimens.xml b/android/java/res/values/brave_dimens.xml index 96cf9b97499..39ab637bcee 100644 --- a/android/java/res/values/brave_dimens.xml +++ b/android/java/res/values/brave_dimens.xml @@ -37,4 +37,12 @@ 200dp 233dp 318dp + + + 56dp + 12dp + 64dp + 36dp + 6dp + 4dp diff --git a/android/javatests/org/chromium/chrome/browser/BytecodeTest.java b/android/javatests/org/chromium/chrome/browser/BytecodeTest.java index d6cc6374be5..7391a16c228 100644 --- a/android/javatests/org/chromium/chrome/browser/BytecodeTest.java +++ b/android/javatests/org/chromium/chrome/browser/BytecodeTest.java @@ -40,8 +40,6 @@ public class BytecodeTest { Assert.assertTrue(classExists( "org/chromium/chrome/browser/search_engines/settings/SearchEngineAdapter")); Assert.assertTrue(classExists("org/chromium/base/CommandLineInitUtil")); - Assert.assertTrue(classExists( - "org/chromium/chrome/browser/toolbar/bottom/BottomToolbarConfiguration")); } @Test @@ -69,9 +67,6 @@ public class BytecodeTest { "org/chromium/chrome/browser/search_engines/settings/SearchEngineAdapter", "sortAndFilterUnnecessaryTemplateUrl")); Assert.assertTrue(methodExists("org/chromium/base/CommandLineInitUtil", "initCommandLine")); - Assert.assertTrue(methodExists( - "org/chromium/chrome/browser/toolbar/bottom/BottomToolbarConfiguration", - "isBottomToolbarEnabled")); } @Test diff --git a/browser/ui/android/strings/android_brave_strings.grd b/browser/ui/android/strings/android_brave_strings.grd index c3d7cd558b7..f75a3ea95b2 100644 --- a/browser/ui/android/strings/android_brave_strings.grd +++ b/browser/ui/android/strings/android_brave_strings.grd @@ -1302,6 +1302,9 @@ until they verify, or until 90 days have passed. Enable "Night Mode" (Experimental) + + Search + Edit Stack diff --git a/build/android/bytecode/BUILD.gn b/build/android/bytecode/BUILD.gn index 732a5cb34d4..e7975c735b8 100644 --- a/build/android/bytecode/BUILD.gn +++ b/build/android/bytecode/BUILD.gn @@ -11,7 +11,6 @@ java_binary("java_bytecode_rewriter") { "//brave/build/android/bytecode/java/org/brave/bytecode/BraveClassAdapter.java", "//brave/build/android/bytecode/java/org/brave/bytecode/BraveClassVisitor.java", "//brave/build/android/bytecode/java/org/brave/bytecode/BraveAndroidSyncSettingsClassAdapter.java", - "//brave/build/android/bytecode/java/org/brave/bytecode/BraveBottomToolbarConfigurationClassAdapter.java", "//brave/build/android/bytecode/java/org/brave/bytecode/BraveCommandLineInitUtilClassAdapter.java", "//brave/build/android/bytecode/java/org/brave/bytecode/BraveEditUrlSuggestionProcessorClassAdapter.java", "//brave/build/android/bytecode/java/org/brave/bytecode/BraveFeedSurfaceCoordinatorClassAdapter.java", diff --git a/build/android/bytecode/java/org/brave/bytecode/BraveBottomToolbarConfigurationClassAdapter.java b/build/android/bytecode/java/org/brave/bytecode/BraveBottomToolbarConfigurationClassAdapter.java deleted file mode 100644 index b090ae5fc5b..00000000000 --- a/build/android/bytecode/java/org/brave/bytecode/BraveBottomToolbarConfigurationClassAdapter.java +++ /dev/null @@ -1,22 +0,0 @@ -/* Copyright (c) 2020 The Brave Authors. All rights reserved. - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.brave.bytecode; - -import org.objectweb.asm.ClassVisitor; - -public class BraveBottomToolbarConfigurationClassAdapter extends BraveClassVisitor { - static String sBottomToolbarConfigurationClassName = - "org/chromium/chrome/browser/toolbar/bottom/BottomToolbarConfiguration"; - - static String sBraveBottomToolbarConfigurationClassName = - "org/chromium/chrome/browser/toolbar/bottom/BraveBottomToolbarConfiguration"; - - public BraveBottomToolbarConfigurationClassAdapter(ClassVisitor visitor) { - super(visitor); - changeMethodOwner(sBottomToolbarConfigurationClassName, "isBottomToolbarEnabled", - sBraveBottomToolbarConfigurationClassName); - } -} diff --git a/build/android/bytecode/java/org/brave/bytecode/BraveClassAdapter.java b/build/android/bytecode/java/org/brave/bytecode/BraveClassAdapter.java index 33912c1c4cd..aa769da8bb4 100644 --- a/build/android/bytecode/java/org/brave/bytecode/BraveClassAdapter.java +++ b/build/android/bytecode/java/org/brave/bytecode/BraveClassAdapter.java @@ -20,7 +20,6 @@ public class BraveClassAdapter { chain = new BraveEditUrlSuggestionProcessorClassAdapter(chain); chain = new BraveSearchEngineAdapterClassAdapter(chain); chain = new BraveCommandLineInitUtilClassAdapter(chain); - chain = new BraveBottomToolbarConfigurationClassAdapter(chain); return chain; } }