Files
cromite/build/patches/Move-navigation-bar-to-bottom.patch

3475 lines
187 KiB
Diff

From: uazo <uazo@users.noreply.github.com>
Date: Tue, 18 Jan 2022 07:43:32 +0000
Subject: Move navigation bar to bottom
Adds an accessibility flag that allows navigation bar to be moved to the bottom.
Unlike what is present in other browsers, this actually moves the bar below,
without creating other elements.
The bottom bar will also be colored as the navigation bar to make the interface
more pleasant and fixes upstream bug 1285311.
Support for tablet mode is also included.
Need: bromite-build-utils.patch
Original License: GPL-2.0-or-later - https://spdx.org/licenses/GPL-2.0-or-later.html
License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html
---
cc/base/features.cc | 8 +
cc/base/features.h | 2 +
cc/input/browser_controls_offset_manager.cc | 8 +
cc/trees/layer_tree_host_impl.cc | 5 +-
.../tab_management/TabGroupUiCoordinator.java | 8 +-
.../tab_management/TabGroupUiMediator.java | 52 +++++-
.../tab_management/TabGroupUiProperties.java | 5 +
.../tab_management/TabGroupUiToolbarView.java | 17 ++
.../tab_management/TabGroupUiViewBinder.java | 3 +
.../TabListContainerViewBinder.java | 2 +-
.../tab_management/TabListCoordinator.java | 148 +++++++++++++++++-
.../TabListEmptyCoordinator.java | 17 +-
.../tab_management/TabSwitcherPaneBase.java | 9 ++
.../browser/feed/FeedSurfaceCoordinator.java | 4 +
.../java/res/layout/search_activity.xml | 1 -
.../android/java/res/xml/main_preferences.xml | 3 +-
.../chrome/browser/ChromeTabbedActivity.java | 3 +-
.../ChromeAccessibilitySettingsDelegate.java | 70 +++++++++
.../HeadlessBrowserControlsStateProvider.java | 5 +
.../BottomBarContainerCoordinator.java | 7 +-
.../compositor/CompositorViewHolder.java | 7 +
.../layouts/LayoutManagerChrome.java | 12 +-
.../layouts/LayoutManagerChromeTablet.java | 7 +-
.../layouts/ToolbarSwipeLayout.java | 14 +-
.../layouts/phone/NewTabAnimationLayout.java | 7 +-
.../overlays/strip/ScrollDelegate.java | 2 +-
.../strip/StripLayoutHelperManager.java | 43 ++++-
.../scene_layer/StaticTabSceneLayer.java | 7 +-
.../scene_layer/TabStripSceneLayer.java | 20 ++-
.../browser/findinpage/FindToolbarTablet.java | 10 +-
.../fullscreen/BrowserControlsManager.java | 20 ++-
.../messages/MessageContainerCoordinator.java | 20 ++-
.../modaldialog/ChromeTabModalPresenter.java | 2 +-
.../chrome/browser/ntp/NewTabPage.java | 14 +-
.../chrome/browser/ntp/NewTabPageLayout.java | 12 +-
.../chrome/browser/ntp/RecentTabsPage.java | 31 +++-
.../browser/page_info/ChromePageInfo.java | 2 +
.../browser/searchwidget/SearchActivity.java | 14 ++
.../settings/FragmentDependencyProvider.java | 5 +
.../StatusIndicatorCoordinator.java | 9 ++
.../StatusIndicatorSceneLayer.java | 6 +-
.../browser/toolbar/ToolbarManager.java | 39 ++++-
.../chrome/browser/ui/BottomContainer.java | 18 +++
.../chrome/browser/ui/BottomSheetManager.java | 10 ++
.../ui/system/StatusBarColorController.java | 8 +
.../android/compositor/layer/toolbar_layer.cc | 6 +-
.../scene_layer/tab_strip_scene_layer.cc | 16 +-
.../BottomControlsStacker.java | 2 +-
.../BrowserControlsMarginAdapter.java | 5 +
.../BrowserControlsStateProvider.java | 6 +
.../BrowserControlsUtils.java | 2 +-
.../browser_controls/TopControlsStacker.java | 6 +-
.../browser/feed/FeedListContentManager.java | 6 +-
.../flags/android/chrome_feature_list.cc | 3 +
.../browser/flags/ChromeFeatureList.java | 10 ++
chrome/browser/hub/internal/BUILD.gn | 5 +
.../chrome/browser/hub/HubCoordinator.java | 9 ++
.../chrome/browser/hub/HubManagerImpl.java | 16 +-
.../browser/hub/ReversedLinearLayout.java | 42 +++++
.../android/res/layout/hub_layout.xml | 9 +-
.../android/res/layout/hub_toolbar_layout.xml | 4 +-
chrome/browser/prefs/browser_prefs.cc | 2 +-
.../chrome/browser/ui/appmenu/AppMenu.java | 39 ++++-
.../ui/appmenu/AppMenuHandlerImpl.java | 10 ++
.../EdgeToEdgeControllerImpl.java | 1 +
.../ui/edge_to_edge/EdgeToEdgeUtils.java | 2 +
...rredIMEWindowInsetApplicationCallback.java | 7 +
.../omnibox/LocationBarCoordinator.java | 1 +
...mniboxSuggestionsDropdownEmbedderImpl.java | 35 +++++
.../suggestions/AutocompleteCoordinator.java | 16 ++
.../suggestions/AutocompleteMediator.java | 6 +-
.../OmniboxSuggestionsDropdown.java | 10 +-
.../OmniboxSuggestionsDropdownEmbedder.java | 6 +
.../base/BaseSuggestionViewBinder.java | 7 +
.../strings/android_chrome_strings.grd | 6 +
chrome/browser/ui/android/toolbar/BUILD.gn | 1 +
.../toolbar/LocationBarFocusScrimHandler.java | 5 +
.../toolbar/ToolbarLongPressMenuHandler.java | 8 -
.../bottom/BottomControlsContentDelegate.java | 11 +-
.../bottom/BottomControlsCoordinator.java | 11 +-
.../bottom/BottomControlsMediator.java | 33 +++-
.../bottom/BottomControlsProperties.java | 6 +-
.../bottom/BottomControlsViewBinder.java | 2 +
.../bottom/ScrollingBottomViewSceneLayer.java | 19 ++-
.../settings/AddressBarPreference.java | 1 +
.../toolbar/top/ToolbarControlContainer.java | 9 ++
.../toolbar/top/TopToolbarCoordinator.java | 2 +-
.../top/TopToolbarOverlayCoordinator.java | 12 +-
.../top/TopToolbarOverlayProperties.java | 8 +-
.../toolbar/top/TopToolbarSceneLayer.java | 14 +-
.../res/xml/accessibility_preferences.xml | 8 +
.../accessibility/AccessibilitySettings.java | 27 ++++
.../AccessibilitySettingsDelegate.java | 8 +
components/viz/common/features.cc | 1 +
.../render_widget_host_view_android.cc | 3 +
.../Move-navigation-bar-to-bottom.inc | 17 ++
.../renderer/core/frame/viewport_data.cc | 1 +
97 files changed, 1111 insertions(+), 107 deletions(-)
create mode 100644 chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/ReversedLinearLayout.java
create mode 100644 cromite_flags/chrome/browser/about_flags_cc/Move-navigation-bar-to-bottom.inc
diff --git a/cc/base/features.cc b/cc/base/features.cc
--- a/cc/base/features.cc
+++ b/cc/base/features.cc
@@ -23,6 +23,14 @@ std::atomic<bool> s_is_eligible_for_throttle_main_frame_to_60hz = false;
BASE_FEATURE(kComputeRasterTranslateForExternalScale,
base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kMoveTopToolbarToBottom,
+ "MoveTopToolbarToBottom",
+ base::FEATURE_DISABLED_BY_DEFAULT);
+
+BASE_FEATURE(kDisableToolbarSwipeUp,
+ "DisableToolbarSwipeUp",
+ base::FEATURE_DISABLED_BY_DEFAULT);
+
BASE_FEATURE(kDeferImplInvalidation, base::FEATURE_ENABLED_BY_DEFAULT);
// TODO(crbug.com/446920991): Reduce back to 1 frame delay once we have a
diff --git a/cc/base/features.h b/cc/base/features.h
--- a/cc/base/features.h
+++ b/cc/base/features.h
@@ -15,6 +15,8 @@
namespace features {
CC_BASE_EXPORT BASE_DECLARE_FEATURE(kComputeRasterTranslateForExternalScale);
+CC_BASE_EXPORT BASE_DECLARE_FEATURE(kMoveTopToolbarToBottom);
+CC_BASE_EXPORT BASE_DECLARE_FEATURE(kDisableToolbarSwipeUp);
// When enabled, the scheduler will allow deferring impl invalidation frames
// for N frames (default 1) to reduce contention with main frames, allowing
diff --git a/cc/input/browser_controls_offset_manager.cc b/cc/input/browser_controls_offset_manager.cc
--- a/cc/input/browser_controls_offset_manager.cc
+++ b/cc/input/browser_controls_offset_manager.cc
@@ -26,6 +26,7 @@
#include "ui/gfx/animation/tween.h"
#include "ui/gfx/geometry/transform.h"
#include "ui/gfx/geometry/vector2d_f.h"
+#include "cc/base/features.h"
namespace cc {
namespace {
@@ -94,6 +95,8 @@ BrowserControlsOffsetManager::BrowserControlsOffsetManager(
BrowserControlsOffsetManager::~BrowserControlsOffsetManager() = default;
float BrowserControlsOffsetManager::ControlsTopOffset() const {
+ if (base::FeatureList::IsEnabled(::features::kMoveTopToolbarToBottom))
+ return 0;
return ContentTopOffset() - TopControlsHeight();
}
@@ -676,6 +679,11 @@ gfx::Vector2dF BrowserControlsOffsetManager::ScrollByPrecise(
// content. If the top controls have no height, the content should scroll
// immediately.
gfx::Vector2dF applied_delta(0.f, old_top_offset - ContentTopOffset());
+ // do not eat scroll offsets if the flag is on, since the content view
+ // top offsets are not changed. It is necessary to synchronize the scroll
+ // with the offset of the user's movement
+ if (base::FeatureList::IsEnabled(::features::kMoveTopToolbarToBottom))
+ return pending_delta;
return pending_delta - applied_delta;
}
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -2806,7 +2806,7 @@ viz::CompositorFrameMetadata LayerTreeHostImpl::MakeCompositorFrameMetadata() {
browser_controls_offset_manager_->TopControlsShownRatio();
metadata.top_controls_visible_height.emplace(visible_height);
-#if BUILDFLAG(IS_ANDROID)
+#if BUILDFLAG(IS_ANDROID) && false
const viz::OffsetTag& top_controls_offset_tag =
browser_controls_offset_manager_->TopControlsOffsetTag();
const viz::OffsetTag& content_offset_tag =
@@ -2844,7 +2844,7 @@ viz::CompositorFrameMetadata LayerTreeHostImpl::MakeCompositorFrameMetadata() {
#endif
}
-#if BUILDFLAG(IS_ANDROID)
+#if BUILDFLAG(IS_ANDROID) && false
if (browser_controls_offset_manager_->BottomControlsHeight() > 0) {
const viz::OffsetTag& bottom_controls_offset_tag =
browser_controls_offset_manager_->BottomControlsOffsetTag();
@@ -5464,6 +5464,7 @@ bool LayerTreeHostImpl::AnimateBrowserControls(base::TimeTicks time) {
return false;
}
+
// This counter-scrolls the page to keep the appearance of the page content
// being fixed while the browser controls animate.
viewport().ScrollBy(scroll_delta,
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
@@ -27,6 +27,7 @@ import org.chromium.base.supplier.NonNullObservableSupplier;
import org.chromium.base.supplier.ObservableSuppliers;
import org.chromium.base.supplier.OneshotSupplier;
import org.chromium.base.supplier.SettableMonotonicObservableSupplier;
+import org.chromium.base.supplier.NullableObservableSupplier;
import org.chromium.base.supplier.SettableNonNullObservableSupplier;
import org.chromium.base.supplier.SettableNullableObservableSupplier;
import org.chromium.build.annotations.Initializer;
@@ -63,6 +64,7 @@ import org.chromium.components.sensitive_content.SensitiveContentFeatures;
import org.chromium.ui.modaldialog.ModalDialogManager;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
+import org.chromium.chrome.browser.theme.TopUiThemeColorProvider;
import java.util.List;
import java.util.function.Supplier;
@@ -201,7 +203,8 @@ public class TabGroupUiCoordinator implements TabGroupUiMediator.ResetHandler, T
@Initializer
public void initializeWithNative(
BottomControlsCoordinator.BottomControlsVisibilityController visibilityController,
- Callback<Object> onSnapshotTokenChange) {
+ Callback<Object> onSnapshotTokenChange,
+ TopUiThemeColorProvider topUiThemeColorProvider, NullableObservableSupplier<Tab> tabSupplier) {
SettableMonotonicObservableSupplier<Object> tabStripTokenSupplier =
ObservableSuppliers.createMonotonic();
@@ -298,7 +301,8 @@ public class TabGroupUiCoordinator implements TabGroupUiMediator.ResetHandler, T
sharedImageTilesConfigBuilder,
mThemeColorProvider,
onSnapshotTokenChange,
- tabStripTokenSupplier);
+ tabStripTokenSupplier,
+ topUiThemeColorProvider, tabSupplier);
if (serviceStatus.isAllowedToJoin()) {
mTabBubbler =
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
@@ -25,6 +25,7 @@ import org.chromium.base.Token;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.base.supplier.LazyOneshotSupplier;
+import org.chromium.base.supplier.NullableObservableSupplier;
import org.chromium.base.supplier.MonotonicObservableSupplier;
import org.chromium.base.supplier.NonNullObservableSupplier;
import org.chromium.base.supplier.ObservableSuppliers;
@@ -79,6 +80,12 @@ import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;
+import org.chromium.chrome.browser.theme.TopUiThemeColorProvider;
+import org.chromium.chrome.browser.tab.CurrentTabObserver;
+import org.chromium.chrome.browser.tab.EmptyTabObserver;
+import org.chromium.chrome.browser.tab.Tab;
+import androidx.annotation.ColorInt;
+
/** A mediator for the TabGroupUi. Responsible for managing the internal state of the component. */
@NullMarked
public class TabGroupUiMediator implements BackPressHandler {
@@ -154,6 +161,11 @@ public class TabGroupUiMediator implements BackPressHandler {
private final Callback<Object> mOnSnapshotTokenChange;
private final MonotonicObservableSupplier<Object> mChildTokenSupplier;
+ private final TopUiThemeColorProvider mTopUiThemeColorProvider;
+
+ /** An observer that watches for changes in the active tab. */
+ private final CurrentTabObserver mTabObserver;
+
// These should only be used when regular (non-incognito) tabs are set in the model.
private final @Nullable SharedImageTilesCoordinator mSharedImageTilesCoordinator;
private final SharedImageTilesConfig.@Nullable Builder mSharedImageTilesConfigBuilder;
@@ -184,7 +196,9 @@ public class TabGroupUiMediator implements BackPressHandler {
SharedImageTilesConfig.@Nullable Builder sharedImageTilesConfigBuilder,
ThemeColorProvider themeColorProvider,
Callback<Object> onSnapshotTokenChange,
- MonotonicObservableSupplier<Object> childTokenSupplier) {
+ MonotonicObservableSupplier<Object> childTokenSupplier,
+ TopUiThemeColorProvider topUiThemeColorProvider, NullableObservableSupplier<Tab> tabSupplier) {
+ mTopUiThemeColorProvider = topUiThemeColorProvider;
mResetHandler = resetHandler;
mModel = model;
mTabModelSelector = tabModelSelector;
@@ -238,11 +252,30 @@ public class TabGroupUiMediator implements BackPressHandler {
mIsShowingHub = true;
}
+ // Keep an observer attached to the visible tab (and only the visible tab) to update
+ // properties including theme color.
+ Callback<Tab> activityTabCallback = (tab) -> {
+ if (tab == null) return;
+ updateThemeColor(tab);
+ };
+ mTabObserver = new CurrentTabObserver(tabSupplier, new EmptyTabObserver() {
+ @Override
+ public void onDidChangeThemeColor(Tab tab, int color) {
+ updateThemeColor(tab);
+ }
+
+ @Override
+ public void onContentChanged(Tab tab) {
+ updateThemeColor(tab);
+ }
+ }, activityTabCallback);
+
// Register for tab model.
mTabModelObserver =
new TabModelObserver() {
@Override
public void didSelectTab(Tab tab, @TabSelectionType int type, int lastId) {
+ updateThemeColor(tab);
resetTabStrip();
}
@@ -264,6 +297,8 @@ public class TabGroupUiMediator implements BackPressHandler {
@Override
public void restoreCompleted() {
resetTabStrip();
+ Tab currentTab = mTabModelSelector.getCurrentTab();
+ updateThemeColor(currentTab);
}
@Override
@@ -361,6 +396,8 @@ public class TabGroupUiMediator implements BackPressHandler {
mModel.set(TabGroupUiProperties.WIDTH_PX_CALLBACK, mWidthPxSupplier::set);
resetTabStrip();
+ mTabObserver.triggerWithCurrentTab();
+
mHandleBackPressChangedSupplier = handleBackPressChangedSupplier;
if (mTabGridDialogControllerSupplier != null) {
mTabGridDialogControllerSupplier.onAvailable(
@@ -394,6 +431,18 @@ public class TabGroupUiMediator implements BackPressHandler {
mModel.set(TINT, mThemeColorProvider.getTint());
}
+ /**
+ * Update the colors of the layer based on the specified tab.
+ * @param tab The tab to base the colors on.
+ */
+ private void updateThemeColor(Tab tab) {
+ if (tab != null) {
+ @ColorInt
+ int color = mTopUiThemeColorProvider.getSceneLayerBackground(tab);
+ mModel.set(TabGroupUiProperties.PRIMARY_COLOR, color);
+ }
+ }
+
private void setupToolbarButtons() {
View.OnClickListener showGroupDialogOnClickListener =
view -> {
@@ -572,6 +621,7 @@ public class TabGroupUiMediator implements BackPressHandler {
@SuppressWarnings("NullAway")
public void destroy() {
+ mTabObserver.destroy();
if (mTabModelSelector != null) {
mTabModelSelector.removeTabGroupModelFilterObserver(mTabModelObserver);
mTabModelSelector.getCurrentTabModelSupplier().removeObserver(mCurrentTabModelObserver);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiProperties.java
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiProperties.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiProperties.java
@@ -13,6 +13,7 @@ import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
+import android.content.res.ColorStateList;
/** {@link PropertyKey} list for the TabGroupUi. */
@NullMarked
@@ -41,6 +42,9 @@ class TabGroupUiProperties {
public static final WritableObjectPropertyKey<Callback<Integer>> WIDTH_PX_CALLBACK =
new WritableObjectPropertyKey<>();
+ public static final WritableObjectPropertyKey<Integer> PRIMARY_COLOR =
+ new WritableObjectPropertyKey<>();
+
public static final PropertyKey[] ALL_KEYS =
new PropertyKey[] {
SHOW_GROUP_DIALOG_ON_CLICK_LISTENER,
@@ -52,5 +56,6 @@ class TabGroupUiProperties {
TINT,
INITIAL_SCROLL_INDEX,
WIDTH_PX_CALLBACK,
+ PRIMARY_COLOR
};
}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
@@ -20,6 +20,10 @@ import org.chromium.build.annotations.Nullable;
import org.chromium.chrome.tab_ui.R;
import org.chromium.ui.widget.ChromeImageView;
+import org.chromium.ui.util.ColorUtils;
+import org.chromium.chrome.browser.theme.ThemeUtils;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+
/** Toolbar for the bottom tab strip see {@link TabGroupUiCoordinator}. */
@NullMarked
public class TabGroupUiToolbarView extends FrameLayout {
@@ -95,6 +99,19 @@ public class TabGroupUiToolbarView extends FrameLayout {
mFadingEdgeEnd.setColorFilter(color, PorterDuff.Mode.SRC_IN);
}
+ void setPrimaryColorAndApplyTint(int color) {
+ if (!ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled())
+ return;
+
+ // change the background color of the bottom bar if the top toolbar is below
+ setContentBackgroundColor(color);
+
+ // and adjust the tint
+ boolean useLightTint = ColorUtils.shouldUseLightForegroundOnBackground(color);
+ ColorStateList tint = ThemeUtils.getThemedToolbarIconTint(getContext(), useLightTint);
+ setTint(tint);
+ }
+
/* package */ void setTint(ColorStateList tint) {
ImageViewCompat.setImageTintList(mShowGroupDialogButton, tint);
ImageViewCompat.setImageTintList(mNewTabButton, tint);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinder.java
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinder.java
@@ -14,6 +14,7 @@ import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiPropert
import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.SHOW_GROUP_DIALOG_ON_CLICK_LISTENER;
import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.TINT;
import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.WIDTH_PX_CALLBACK;
+import static org.chromium.chrome.browser.tasks.tab_management.TabGroupUiProperties.PRIMARY_COLOR;
import android.view.View;
@@ -54,6 +55,8 @@ class TabGroupUiViewBinder {
} else if (NEW_TAB_BUTTON_ON_CLICK_LISTENER == propertyKey) {
viewHolder.toolbarView.setNewTabButtonOnClickListener(
model.get(NEW_TAB_BUTTON_ON_CLICK_LISTENER));
+ } else if (PRIMARY_COLOR == propertyKey) {
+ viewHolder.toolbarView.setPrimaryColorAndApplyTint(model.get(PRIMARY_COLOR));
} else if (IS_MAIN_CONTENT_VISIBLE == propertyKey) {
viewHolder.toolbarView.setMainContentVisibility(model.get(IS_MAIN_CONTENT_VISIBLE));
} else if (BACKGROUND_COLOR == propertyKey) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinder.java
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinder.java
@@ -251,7 +251,7 @@ class TabListContainerViewBinder {
.getDimensionPixelSize(R.dimen.hub_search_box_gap);
recyclerView.setPadding(
recyclerView.getPaddingLeft(),
- paddingTop,
+ /*paddingTop*/ 0,
recyclerView.getPaddingRight(),
recyclerView.getPaddingBottom());
} else if (IS_NON_ZERO_Y_OFFSET == propertyKey) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
@@ -10,8 +10,14 @@ import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.Card
import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.CardProperties.ModelType.TAB;
import static org.chromium.chrome.browser.tasks.tab_management.TabProperties.TAB_ID;
+import android.os.Parcel;
+import android.os.Parcelable;
+
import android.app.Activity;
import android.graphics.PointF;
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.util.Size;
import android.view.InputDevice;
@@ -78,6 +84,8 @@ import org.chromium.ui.modelutil.SimpleRecyclerViewAdapter;
import org.chromium.ui.recyclerview.widget.ItemTouchHelper2;
import org.chromium.ui.widget.ViewLookupCachingFrameLayout;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -166,6 +174,124 @@ public class TabListCoordinator implements PriceWelcomeMessageProvider, DestroyO
private int mAwaitingTabId = Tab.INVALID_TAB_ID;
private @TabActionState int mTabActionState;
+ public class GridLayoutManagerDockBottom extends GridLayoutManager {
+ public static class SavedState implements Parcelable {
+ private Parcelable superState;
+ private int mTopPadding;
+
+ public SavedState() {}
+
+ public SavedState(Parcel in) {
+ superState = in.readParcelable(GridLayoutManager.class.getClassLoader());
+ mTopPadding = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(superState, flags);
+ dest.writeInt(mTopPadding);
+ }
+
+ public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
+ @Override
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ @Override
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
+ Context mContext;
+
+ TabListRecyclerView mRecyclerView;
+
+ final int MAX_TOP_PADDING = 99999;
+ int mTopPadding = MAX_TOP_PADDING;
+
+ int mLastPosition = -1;
+ boolean mIsFirstLayout = true;
+
+ public GridLayoutManagerDockBottom(Context context, int spanCount) {
+ super(context, spanCount);
+ mContext = context;
+ }
+
+ public void setTabListRecyclerView(TabListRecyclerView recyclerView) {
+ mRecyclerView = recyclerView;
+ }
+
+ public void ResetTopPosition() {
+ mIsFirstLayout = true;
+ }
+
+ @Override
+ public int getPaddingTop() {
+ if (mTopPadding == MAX_TOP_PADDING) return super.getPaddingTop();
+ return mTopPadding;
+ }
+
+ @Override
+ public int getPaddingBottom() {
+ return 1;
+ }
+
+ @Override
+ public void scrollToPositionWithOffset(int position, int offset) {
+ mLastPosition = position;
+ super.scrollToPositionWithOffset(position, offset - getPaddingTop());
+ }
+
+ @Override
+ public void onLayoutCompleted(RecyclerView.State state) {
+ super.onLayoutCompleted(state);
+ checkAwaitingLayout();
+
+ if (state.isPreLayout() || state.isMeasuring()) return;
+ View lastView = findViewByPosition(findFirstVisibleItemPosition());
+ if (lastView != null) {
+ if (mTopPadding == 0) mTopPadding = MAX_TOP_PADDING;
+ mTopPadding = Math.min(mTopPadding, mRecyclerView.getHeight() - lastView.getHeight());
+ if (mIsFirstLayout) {
+ mIsFirstLayout = false;
+ scrollToPositionWithOffset(mLastPosition, getPaddingTop() + getPaddingBottom());
+ }
+ }
+
+ if (mLastPosition >= state.getItemCount()) {
+ ResetTopPosition();
+ scrollToPositionWithOffset(state.getItemCount()-getSpanCount(),
+ getPaddingTop() + getPaddingBottom());
+ }
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ SavedState ss = new SavedState();
+ ss.superState = super.onSaveInstanceState();
+ ss.mTopPadding = mTopPadding;
+ return ss;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ if (state instanceof SavedState) {
+ SavedState ss = (SavedState) state;
+ mTopPadding = ss.mTopPadding;
+ state = ss.superState;
+ }
+ super.onRestoreInstanceState(state);
+ }
+ }
+
/**
* Construct a coordinator for UI that shows a list of tabs.
*
@@ -399,6 +525,12 @@ public class TabListCoordinator implements PriceWelcomeMessageProvider, DestroyO
checkAwaitingLayout();
}
};
+ if (actionOnRelatedTabs && ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ gridLayoutManager =
+ new GridLayoutManagerDockBottom(activity, GRID_LAYOUT_SPAN_COUNT_COMPACT);
+ ((GridLayoutManagerDockBottom)gridLayoutManager)
+ .setTabListRecyclerView(mRecyclerView);
+ }
mRecyclerView.setLayoutManager(gridLayoutManager);
mMediator.registerOrientationListener(gridLayoutManager);
mMediator.updateSpanCount(
@@ -418,6 +550,17 @@ public class TabListCoordinator implements PriceWelcomeMessageProvider, DestroyO
}
};
mRecyclerView.setLayoutManager(layoutManager);
+ } else if (mMode == TabListMode.LIST) {
+ LinearLayoutManager layout =
+ new LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false) {
+ @Override
+ public void onLayoutCompleted(RecyclerView.State state) {
+ super.onLayoutCompleted(state);
+ checkAwaitingLayout();
+ }
+ };
+ layout.setStackFromEnd(ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled());
+ mRecyclerView.setLayoutManager(layout);
}
mMediator.setRecyclerViewItemAnimationToggle(mRecyclerView::setDisableItemAnimations);
}
@@ -440,7 +583,7 @@ public class TabListCoordinator implements PriceWelcomeMessageProvider, DestroyO
mRecyclerView,
emptyViewParent,
mModelList,
- this::runOnItemAnimatorFinished);
+ this::runOnItemAnimatorFinished, mBrowserControlsStateProvider);
}
mTabListHighlighter = new TabListHighlighter(mModelList);
mTabListMergeAnimationManager = new TabListMergeAnimationManager(mRecyclerView);
@@ -831,6 +974,9 @@ public class TabListCoordinator implements PriceWelcomeMessageProvider, DestroyO
void prepareTabSwitcherPaneView() {
registerLayoutChangeListener();
mRecyclerView.setupCustomItemAnimator();
+ if (mRecyclerView.getLayoutManager() instanceof GridLayoutManagerDockBottom) {
+ ((GridLayoutManagerDockBottom)mRecyclerView.getLayoutManager()).ResetTopPosition();
+ }
}
private void initializeEmptyStateView() {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEmptyCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEmptyCoordinator.java
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEmptyCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEmptyCoordinator.java
@@ -14,7 +14,9 @@ import android.view.View.OnLayoutChangeListener;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
+import android.widget.FrameLayout;
+import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
import androidx.annotation.DrawableRes;
import androidx.annotation.Px;
import androidx.annotation.StringRes;
@@ -28,6 +30,7 @@ import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.tab_ui.R;
import org.chromium.ui.modelutil.ListObservable;
import org.chromium.ui.modelutil.ListObservable.ListObserver;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
/**
* Empty coordinator that is responsible for showing an empty state view in tab switcher when we are
@@ -60,12 +63,14 @@ class TabListEmptyCoordinator {
private boolean mIsTabSwitcherShowing;
private boolean mIsListObserverAttached;
private @Nullable TabListEmptyIllustrationAnimationManager mIllustrationAnimationManager;
+ private BrowserControlsStateProvider mBrowserControlsStateProvider;
public TabListEmptyCoordinator(
TabListRecyclerView recyclerView,
ViewGroup rootView,
TabListModel model,
- Callback<Runnable> runOnItemAnimatorFinished) {
+ Callback<Runnable> runOnItemAnimatorFinished,
+ BrowserControlsStateProvider browserControlsStateProvider) {
mRecyclerView = recyclerView;
mRootView = rootView;
mContext = rootView.getContext();
@@ -73,6 +78,7 @@ class TabListEmptyCoordinator {
// Observe TabListModel to determine when to add / remove empty state view.
mModel = model;
+ mBrowserControlsStateProvider = browserControlsStateProvider;
mListObserver =
new ListObserver<>() {
@Override
@@ -218,6 +224,15 @@ class TabListEmptyCoordinator {
public void attachEmptyView() {
if (mEmptyView != null && mEmptyView.getParent() == null) {
mRootView.addView(mEmptyView);
+ FrameLayout.LayoutParams emptyViewParams =
+ (FrameLayout.LayoutParams) mEmptyView.getLayoutParams();
+ int toolbarHeightPx = mBrowserControlsStateProvider.getTopControlsHeight();
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ emptyViewParams.bottomMargin = toolbarHeightPx;
+ } else {
+ emptyViewParams.topMargin = toolbarHeightPx;
+ }
+ mEmptyView.setLayoutParams(emptyViewParams);
setEmptyViewVisibility(View.GONE);
}
}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneBase.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneBase.java
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneBase.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneBase.java
@@ -45,6 +45,7 @@ import org.chromium.build.BuildConfig;
import org.chromium.build.annotations.NullMarked;
import org.chromium.build.annotations.Nullable;
import org.chromium.chrome.browser.compositor.CompositorViewHolder;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.hub.FadeHubLayoutAnimationFactory;
import org.chromium.chrome.browser.hub.HubContainerView;
import org.chromium.chrome.browser.hub.HubLayoutAnimationListener;
@@ -149,6 +150,7 @@ public abstract class TabSwitcherPaneBase extends PaneBase
private @Nullable PaneHubController mPaneHubController;
private @Nullable Long mWaitForTabStateInitializedStartTimeMs;
private final NonNullObservableSupplier<Boolean> mXrSpaceModeObservableSupplier;
+ private boolean isFirstRequest = true;
/**
* @param context The activity context.
@@ -476,6 +478,13 @@ public abstract class TabSwitcherPaneBase extends PaneBase
Log.d(TAG, "Geometry not ready using fallback animation.");
useFallbackAnimation = true;
}
+ if (isFirstRequest) {
+ isFirstRequest = false;
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ // skip first tab animation since rects are fake
+ return;
+ }
+ }
// Ignore left offset and just ensure the width is correct. See crbug/1502437.
initialRect.offset(-initialLeftOffset, -initialTopOffset);
finalRect.offset(-finalLeftOffset, -finalTopOffset);
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
@@ -18,6 +18,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.view.LayoutInflater;
+import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -672,6 +673,9 @@ public class FeedSurfaceCoordinator
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.setMarginStart(margin);
layoutParams.setMarginEnd(margin);
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ layoutParams.gravity = Gravity.BOTTOM;
+ }
mNtpHeader.setLayoutParams(layoutParams);
}
diff --git a/chrome/android/java/res/layout/search_activity.xml b/chrome/android/java/res/layout/search_activity.xml
--- a/chrome/android/java/res/layout/search_activity.xml
+++ b/chrome/android/java/res/layout/search_activity.xml
@@ -15,7 +15,6 @@ found in the LICENSE file.
android:id="@+id/omnibox_results_container_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
- app:layout_anchor="@id/toolbar"
android:background="@android:color/white"
app:layout="@layout/omnibox_results_container"/>
diff --git a/chrome/android/java/res/xml/main_preferences.xml b/chrome/android/java/res/xml/main_preferences.xml
--- a/chrome/android/java/res/xml/main_preferences.xml
+++ b/chrome/android/java/res/xml/main_preferences.xml
@@ -44,7 +44,8 @@ found in the LICENSE file.
android:fragment="org.chromium.chrome.browser.toolbar.settings.AddressBarSettingsFragment"
android:key="address_bar"
android:order="7"
- android:title="@string/address_bar_settings"/>
+ android:title="@string/address_bar_settings"
+ app:isPreferenceVisible="false"/>
<Preference
android:fragment="org.chromium.chrome.browser.privacy.settings.PrivacySettings"
android:key="privacy"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -1123,7 +1123,8 @@ public class ChromeTabbedActivity extends ChromeActivity implements PreAttachInt
getSnackbarManager(),
/* glicClickHandler= */ () ->
((TabbedRootUiCoordinator) mRootUiCoordinator)
- .toggleGlic(false));
+ .toggleGlic(),
+ /*browserControlsManagerSupplier*/ () -> getBrowserControlsManager(false));
mLayoutStateProviderSupplier.set(mLayoutManager);
}
}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/ChromeAccessibilitySettingsDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/ChromeAccessibilitySettingsDelegate.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/ChromeAccessibilitySettingsDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/ChromeAccessibilitySettingsDelegate.java
@@ -22,6 +22,14 @@ import org.chromium.chrome.browser.flags.CromiteNativeUtils;
import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
+import org.chromium.chrome.R;
+import android.app.Activity;
+import java.util.function.Supplier;
+import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
+import org.chromium.chrome.browser.ui.messages.snackbar.INeedSnackbarManager;
+import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
+import org.chromium.chrome.browser.lifetime.ApplicationLifetime;
+
/** The Chrome implementation of AccessibilitySettingsDelegate. */
@NullMarked
public class ChromeAccessibilitySettingsDelegate implements AccessibilitySettingsDelegate {
@@ -86,6 +94,12 @@ public class ChromeAccessibilitySettingsDelegate implements AccessibilitySetting
return new ForceTabletUIDelegate();
}
+ private Supplier<SnackbarManager> mSnackbarManagerSupplier;
+
+ public void setSnackbarManagerSupplier(Supplier<SnackbarManager> snackbarManager) {
+ mSnackbarManagerSupplier = snackbarManager;
+ }
+
private final Profile mProfile;
/**
@@ -97,6 +111,62 @@ public class ChromeAccessibilitySettingsDelegate implements AccessibilitySetting
mProfile = profile;
}
+ private static class MoveTopToolbarToBottomDelegate implements BooleanPreferenceDelegate {
+ @Override
+ public boolean getValue() {
+ return ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled();
+ }
+
+ @Override
+ public void setValue(boolean value) {
+ CromiteNativeUtils.setFlagEnabled(ChromeFeatureList.MOVE_TOP_TOOLBAR_TO_BOTTOM,
+ "move-top-toolbar-to-bottom", value);
+ }
+ }
+
+ private static class DisableToolbarSwipeUpDelegate implements BooleanPreferenceDelegate {
+ @Override
+ public boolean getValue() {
+ return ChromeFeatureList.sDisableToolbarSwipeUp.isEnabled();
+ }
+
+ @Override
+ public void setValue(boolean value) {
+ CromiteNativeUtils.setFlagEnabled(ChromeFeatureList.DISABLE_TOOLBAR_SWIPE_UP,
+ "disable-toolbar-swipe-up", value);
+ }
+ }
+
+ @Override
+ public BooleanPreferenceDelegate getMoveTopToolbarToBottomDelegate() {
+ return new MoveTopToolbarToBottomDelegate();
+ }
+
+ @Override
+ public BooleanPreferenceDelegate getDisableToolbarSwipeUpDelegate() {
+ return new DisableToolbarSwipeUpDelegate();
+ }
+
+ @Override
+ public void requestRestart(Activity activity) {
+ Snackbar mSnackbar = Snackbar.make(activity.getString(R.string.ui_relaunch_notice),
+ new SnackbarManager.SnackbarController() {
+ @Override
+ public void onDismissNoAction(Object actionData) { }
+
+ @Override
+ public void onAction(Object actionData) {
+ ApplicationLifetime.terminate(true);
+ }
+ }, Snackbar.TYPE_NOTIFICATION, Snackbar.UMA_UNKNOWN)
+ .setDefaultLines(false)
+ .setAction(activity.getString(R.string.relaunch),
+ /*actionData*/null)
+ .setDuration(/*durationMs*/70000);
+ if (!mSnackbarManagerSupplier.get().isShowing())
+ mSnackbarManagerSupplier.get().showSnackbar(mSnackbar);
+ }
+
@Override
public BrowserContextHandle getBrowserContextHandle() {
return mProfile;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/HeadlessBrowserControlsStateProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/HeadlessBrowserControlsStateProvider.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/HeadlessBrowserControlsStateProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/HeadlessBrowserControlsStateProvider.java
@@ -25,6 +25,11 @@ public class HeadlessBrowserControlsStateProvider implements BrowserControlsStat
return 0;
}
+ @Override
+ public int getTopControlsHeightRealOffset() {
+ return 0;
+ }
+
@Override
public int getTopControlsMinHeight() {
return 0;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bottombar/BottomBarContainerCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/bottombar/BottomBarContainerCoordinator.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/bottombar/BottomBarContainerCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bottombar/BottomBarContainerCoordinator.java
@@ -22,6 +22,10 @@ import org.chromium.chrome.browser.ui.bottombar.BottomBar;
import org.chromium.chrome.browser.ui.bottombar.BottomBarHostManager.Host;
import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
+import org.chromium.base.supplier.NullableObservableSupplier;
+import org.chromium.chrome.browser.theme.TopUiThemeColorProvider;
+import org.chromium.chrome.browser.tab.Tab;
+
/**
* Container for the bottom bar.
*
@@ -76,7 +80,8 @@ public class BottomBarContainerCoordinator implements BottomControlsContentDeleg
@Override
public void initializeWithNative(
BottomControlsVisibilityController visibilityController,
- Callback<Object> onModelTokenChange) {
+ Callback<Object> onModelTokenChange,
+ TopUiThemeColorProvider topUiThemeColorProvider, NullableObservableSupplier<Tab> tabSupplier) {
mVisibilityController = visibilityController;
mOnModelTokenChange = onModelTokenChange;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
@@ -289,6 +289,10 @@ public class CompositorViewHolder extends FrameLayout
@Override
public void setCurrentTouchEventOffsets(float left, float top) {
EventForwarder forwarder = getEventForwarder();
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ // no need to adjust the touch offsets, since the content view is never moved
+ top = 0;
+ }
if (forwarder != null) {
forwarder.setCurrentTouchOffsetX(left);
forwarder.setCurrentTouchOffsetY(top);
@@ -1043,6 +1047,9 @@ public class CompositorViewHolder extends FrameLayout
mApplicationBottomInsetSupplier != null
? mApplicationBottomInsetSupplier.getInsets().webContentsHeightInset
: 0;
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ keyboardInset = 0;
+ }
int verticalViewportInsets = controlsInsets + keyboardInset;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
@@ -47,6 +47,7 @@ import org.chromium.components.browser_ui.widget.gesture.SwipeGestureListener.Sw
import org.chromium.ui.base.DeviceFormFactor;
import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
import org.chromium.ui.util.AccessibilityUtil;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
import java.util.List;
import java.util.function.Supplier;
@@ -165,7 +166,9 @@ public class LayoutManagerChrome extends LayoutManagerImpl implements Accessibil
@Override
public SwipeHandler createToolbarSwipeHandler(boolean supportsSwipeToShowTabSwitcher) {
- return new ToolbarSwipeHandler(supportsSwipeToShowTabSwitcher);
+ return new ToolbarSwipeHandler(
+ supportsSwipeToShowTabSwitcher || ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled(),
+ !ChromeFeatureList.sDisableToolbarSwipeUp.isEnabled());
}
@Override
@@ -401,9 +404,11 @@ public class LayoutManagerChrome extends LayoutManagerImpl implements Accessibil
private static final float SWIPE_RANGE_DEG = 25;
private final boolean mSupportsSwipeToShowTabSwitcher;
+ private final boolean mSupportSwipeUp;
- public ToolbarSwipeHandler(boolean supportsSwipeToShowTabSwitcher) {
+ public ToolbarSwipeHandler(boolean supportsSwipeToShowTabSwitcher, boolean supportSwipeUp) {
mSupportsSwipeToShowTabSwitcher = supportsSwipeToShowTabSwitcher;
+ mSupportSwipeUp = supportSwipeUp;
}
@Override
@@ -523,7 +528,8 @@ public class LayoutManagerChrome extends LayoutManagerImpl implements Accessibil
return false;
}
- return direction == ScrollDirection.LEFT || direction == ScrollDirection.RIGHT;
+ return direction == ScrollDirection.LEFT || direction == ScrollDirection.RIGHT
+ || (direction == ScrollDirection.UP && mSupportSwipeUp);
}
}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
@@ -44,6 +44,7 @@ import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.dragdrop.DragAndDropDelegate;
import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
import org.chromium.ui.xr.scenecore.XrSceneCoreSessionManager;
+import org.chromium.chrome.browser.fullscreen.BrowserControlsManager;
import java.util.function.Supplier;
@@ -122,7 +123,8 @@ public class LayoutManagerChromeTablet extends LayoutManagerChrome {
@Nullable XrSceneCoreSessionManager xrSceneCoreSessionManager,
BackPressManager backPressManager,
SnackbarManager snackbarManager,
- Runnable glicClickHandler) {
+ Runnable glicClickHandler,
+ Supplier<BrowserControlsManager> browserControlsManagerSupplier) {
super(
host,
contentContainer,
@@ -163,7 +165,8 @@ public class LayoutManagerChromeTablet extends LayoutManagerChrome {
xrSpaceModeObservableSupplier,
backPressManager,
snackbarManager,
- glicClickHandler);
+ glicClickHandler,
+ browserControlsManagerSupplier);
addSceneOverlay(mTabStripLayoutHelperManager);
addObserver(mTabStripLayoutHelperManager.getTabSwitcherObserver());
mDesktopWindowStateManager = desktopWindowStateManager;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
@@ -57,6 +57,8 @@ import org.chromium.ui.base.LocalizationUtils;
import org.chromium.ui.interpolators.Interpolators;
import org.chromium.ui.resources.ResourceManager;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+
import java.util.ArrayList;
import java.util.List;
@@ -541,7 +543,11 @@ public class ToolbarSwipeLayout extends Layout {
mLeftToolbarOverlay.setXOffset(leftX * dpToPx);
}
mLeftTab.setX(leftX);
- mLeftTab.setY(mBrowserControlsStateProvider.getContentOffset() / dpToPx);
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ mLeftTab.setY(0);
+ } else {
+ mLeftTab.setY(mBrowserControlsStateProvider.getContentOffset() / dpToPx);
+ }
needUpdate = updateSnap(dt, mLeftTab) || needUpdate;
} else if (mLeftToolbarOverlay != null) {
mLeftToolbarOverlay.setManualVisibility(false);
@@ -554,7 +560,11 @@ public class ToolbarSwipeLayout extends Layout {
mRightToolbarOverlay.setXOffset(rightX * dpToPx);
}
mRightTab.setX(rightX);
- mRightTab.setY(mBrowserControlsStateProvider.getContentOffset() / dpToPx);
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ mRightTab.setY(0);
+ } else {
+ mRightTab.setY(mBrowserControlsStateProvider.getContentOffset() / dpToPx);
+ }
needUpdate = updateSnap(dt, mRightTab) || needUpdate;
} else if (mRightToolbarOverlay != null) {
mRightToolbarOverlay.setManualVisibility(false);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayout.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayout.java
@@ -461,6 +461,10 @@ public class NewTabAnimationLayout extends Layout {
boolean oldTabHasTopToolbar = ToolbarPositionController.shouldShowToolbarOnTop(oldTab);
boolean newTabHasTopToolbar = ToolbarPositionController.shouldShowToolbarOnTop(newTab);
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ oldTabHasTopToolbar = false;
+ newTabHasTopToolbar = false;
+ }
if (oldTabHasTopToolbar && newTabHasTopToolbar) {
return RectStart.TOP_TOOLBAR;
@@ -697,7 +701,8 @@ public class NewTabAnimationLayout extends Layout {
int[] toolbarPosition = new int[2];
mAnimationHostView.findViewById(R.id.toolbar).getLocationInWindow(toolbarPosition);
boolean isTopToolbar =
- isRegularNtp || ToolbarPositionController.shouldShowToolbarOnTop(animationTab);
+ isRegularNtp || ToolbarPositionController.shouldShowToolbarOnTop(animationTab)
+ || !ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled();
int toolbarHeight = toolbarPosition[1] + getTopInsetIfNeeded(animationTab);
Rect compositorViewRect = new Rect();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollDelegate.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollDelegate.java
@@ -351,7 +351,7 @@ public class ScrollDelegate {
boolean useUnadjustedScrollOffset = isRtl != isLeft;
float scrollOffset = mScrollOffset;
- return -(useUnadjustedScrollOffset ? scrollOffset : (mScrollOffsetLimit - scrollOffset));
+ return -Math.abs(useUnadjustedScrollOffset ? scrollOffset : (mScrollOffsetLimit - scrollOffset));
}
/**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
@@ -129,6 +129,9 @@ import org.chromium.ui.interpolators.Interpolators;
import org.chromium.ui.resources.ResourceManager;
import org.chromium.ui.util.ColorUtils;
import org.chromium.url.GURL;
+import org.chromium.chrome.browser.fullscreen.BrowserControlsManager;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import java.util.function.Supplier;
import java.util.ArrayList;
import java.util.List;
@@ -317,12 +320,17 @@ public class StripLayoutHelperManager
// Drag-Drop
private @Nullable TabStripDragHandler mTabStripDragHandler;
+ private final Supplier<BrowserControlsManager> mBrowserControlsManagerSupplier;
+ private final float mDpToPx;
+ private RectF mCachedViewport;
+
private class TabStripEventHandler implements AreaMotionEventHandler {
@Override
public void onDown(float x, float y, int buttons) {
if (DragDropGlobalState.hasValue()) {
return;
}
+ y -= mStripFilterArea.top;
if (mGlicButton != null && mGlicButton.onDown(x, y, buttons)) {
return;
}
@@ -354,6 +362,7 @@ public class StripLayoutHelperManager
if (DragDropGlobalState.hasValue()) {
return;
}
+ y -= mStripFilterArea.top;
if (mGlicButton != null) {
mGlicButton.drag(x, y);
}
@@ -368,6 +377,7 @@ public class StripLayoutHelperManager
if (DragDropGlobalState.hasValue()) {
return;
}
+ y -= mStripFilterArea.top;
long time = time();
if (mGlicButton != null && mGlicButton.click(x, y, buttons)) {
mGlicButton.handleClick(time, buttons, modifiers);
@@ -385,6 +395,7 @@ public class StripLayoutHelperManager
if (DragDropGlobalState.hasValue()) {
return;
}
+ y -= mStripFilterArea.top;
getActiveStripLayoutHelper().fling(time(), velocityX);
}
@@ -423,6 +434,7 @@ public class StripLayoutHelperManager
if (DragDropGlobalState.hasValue()) {
return;
}
+ y -= mStripFilterArea.top;
getActiveStripLayoutHelper().onHoverMove(x, y);
}
@@ -545,7 +557,8 @@ public class StripLayoutHelperManager
@Nullable NonNullObservableSupplier<Boolean> xrSpaceModeObservableSupplier,
BackPressManager backPressManager,
SnackbarManager snackbarManager,
- Runnable glicClickHandler) {
+ Runnable glicClickHandler,
+ Supplier<BrowserControlsManager> browserControlsManagerSupplier) {
mContext = context;
Resources res = context.getResources();
mManagerHost = managerHost;
@@ -702,6 +715,8 @@ public class StripLayoutHelperManager
shareDelegateSupplier,
TabGroupListBottomSheetCoordinator::new,
snackbarManager);
+ mBrowserControlsManagerSupplier = browserControlsManagerSupplier;
+ mDpToPx = context.getResources().getDisplayMetrics().density;
tabHoverCardViewStub.setOnInflateListener(
(viewStub, view) -> {
@@ -1059,6 +1074,7 @@ public class StripLayoutHelperManager
RectF viewport, RectF visibleViewport, ResourceManager resourceManager) {
assert mTabStripTreeProvider != null;
mResourceManager = resourceManager;
+ mCachedViewport = viewport;
// When refactor is enabled, the mSceneLayerYOffset / mSceneLayerVisibleHeight wil be
// calculated externally, so we can skip the adjustment here.
@@ -1103,6 +1119,10 @@ public class StripLayoutHelperManager
getStripTransitionScrimColor(), mStripTransitionScrimOpacity);
}
+ int topControlsHeight = 0;
+ if (mBrowserControlsManagerSupplier.get() != null) {
+ topControlsHeight = mBrowserControlsManagerSupplier.get().getTopControlsHeight();
+ }
mTabStripTreeProvider.pushAndUpdateStrip(
this,
assertNonNull(mLayerTitleCacheSupplier.get()),
@@ -1116,7 +1136,9 @@ public class StripLayoutHelperManager
mStripTransitionScrimOpacity,
getActiveStripLayoutHelper().getLeftPaddingToDraw(),
getActiveStripLayoutHelper().getRightPaddingToDraw(),
- mTopPadding);
+ mTopPadding,
+ mCachedViewport.height(),
+ topControlsHeight);
}
private float getVisibleHeightDp(float topControlOffsetDp) {
@@ -1221,11 +1243,21 @@ public class StripLayoutHelperManager
mRightPadding,
mTopPadding);
+ float top = mTopPadding;
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled() &&
+ mBrowserControlsManagerSupplier.get() != null) {
+ // move the rectangle to grab the touch events as the tab list (in tablet mode)
+ // is down and is following the toolbar offset as it moves.
+ // values are in pixels.
+ top = height - ((mBrowserControlsManagerSupplier.get().getTopControlsHeight()
+ - mBrowserControlsManagerSupplier.get().getTopControlOffset()) / mDpToPx);
+ visibleViewportOffsetY = mHeight;
+ }
mStripFilterArea.set(
mLeftPadding,
- mTopPadding,
+ top,
mWidth - mRightPadding,
- Math.min(getHeight(), visibleViewportOffsetY));
+ top + Math.min(getHeight(), visibleViewportOffsetY));
// Avoid handling motion events when invisible strip state persists after a size change.
if (mEventFilter != null
&& getStripVisibilityStateSupplier().get() == StripVisibilityState.VISIBLE) {
@@ -1262,7 +1294,8 @@ public class StripLayoutHelperManager
// TODO(crbug/331490430): Revisit how we position the scrollable strip.
mTopPadding = Math.max(0, mHeight - mScrollableStripHeight);
- onSizeChanged(mWidth, mHeight, mLastVisibleViewportOffsetY, mOrientation);
+ if (!ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled())
+ onSizeChanged(mWidth, mHeight, mLastVisibleViewportOffsetY, mOrientation);
}
}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/StaticTabSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/StaticTabSceneLayer.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/StaticTabSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/StaticTabSceneLayer.java
@@ -19,6 +19,8 @@ import org.chromium.chrome.browser.tab_ui.TabContentManager;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+
/** A SceneLayer to render a static tab. */
@NullMarked
@JNINamespace("android")
@@ -55,7 +57,10 @@ public class StaticTabSceneLayer extends SceneLayer {
float y =
model.get(LayoutTab.CONTENT_OFFSET_Y)
+ model.get(LayoutTab.RENDER_Y) * LayoutTab.sDpToPx;
-
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ // the page content window never moves, it is fixed at the top
+ y = 0;
+ }
// Check isActiveLayout to prevent pushing a TAB_ID for a static layer that may already be
// invalidated by the next layout.
StaticTabSceneLayerJni.get()
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java
@@ -35,6 +35,9 @@ import org.chromium.chrome.browser.tab.TabUtils;
import org.chromium.chrome.browser.tasks.tab_management.TabUiThemeUtil;
import org.chromium.components.tab_groups.TabGroupColorId;
import org.chromium.ui.resources.ResourceManager;
+import org.chromium.ui.base.DeviceFormFactor;
+import org.chromium.base.ContextUtils;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
/**
* The Java component of what is basically a CC Layer that manages drawing the Tab Strip (which is
@@ -119,10 +122,23 @@ public class TabStripSceneLayer extends SceneOverlayLayer {
float scrimOpacity,
float leftPaddingDp,
float rightPaddingDp,
- float topPaddingDp) {
+ float topPaddingDp,
+ float viewportHeight,
+ int topControlsHeight) {
if (mNativePtr == 0) return;
- final boolean visible = yOffset > -layoutHelper.getHeight();
+ boolean visible = yOffset > -layoutHelper.getHeight();
+
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ if (DeviceFormFactor.isNonMultiDisplayContextOnTablet(ContextUtils.getApplicationContext())) {
+ // the list of open tabs is moved down, above the top
+ // toolbar which is also below.
+ // values are in pixel.
+ yOffset = (((int)viewportHeight - topControlsHeight) / mDpToPx) - yOffset;
+ // and it disappears along with the moving toolbar with a higher range
+ visible = yOffset > (-layoutHelper.getHeight() - topControlsHeight);
+ }
+ }
if (DeviceFormFactor.isNonMultiDisplayContextOnTablet(ContextUtils.getApplicationContext())) {
TintedCompositorButton newTabButton = layoutHelper.getNewTabButton();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarTablet.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarTablet.java
@@ -15,6 +15,8 @@ import android.view.View;
import org.chromium.build.annotations.NullMarked;
import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+
import org.chromium.chrome.R;
import org.chromium.components.browser_ui.widget.animation.CancelAwareAnimatorListener;
import org.chromium.ui.interpolators.Interpolators;
@@ -172,9 +174,11 @@ public class FindToolbarTablet extends FindToolbar {
if (show && getVisibility() != View.VISIBLE && mCurrentAnimation != mAnimationEnter) {
View anchorView = getRootView().findViewById(R.id.toolbar);
- var lp = (MarginLayoutParams) getLayoutParams();
- lp.topMargin = anchorView.getBottom() - mYInsetPx;
- setLayoutParams(lp);
+ if (!ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ var lp = (MarginLayoutParams) getLayoutParams();
+ lp.topMargin = anchorView.getBottom() - mYInsetPx;
+ setLayoutParams(lp);
+ }
nextAnimator = mAnimationEnter;
} else if (!show && getVisibility() != View.GONE && mCurrentAnimation != mAnimationLeave) {
nextAnimator = mAnimationLeave;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java
@@ -55,6 +55,8 @@ import org.chromium.ui.OffsetTagConstraints;
import org.chromium.ui.base.ViewUtils;
import org.chromium.ui.util.TokenHolder;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+
/** A class that manages browser control visibility and positioning. */
@NullMarked
public class BrowserControlsManager implements ActivityStateListener, BrowserControlsSizer {
@@ -364,6 +366,9 @@ public class BrowserControlsManager implements ActivityStateListener, BrowserCon
mRendererTopContentOffset = mTopControlsHeight;
updateControlOffset();
scheduleVisibilityUpdate();
+ for (BrowserControlsStateProvider.Observer obs : mControlsObservers) {
+ obs.onTopControlsHeightChanged(mTopControlsHeight, mTopControlsMinHeight);
+ }
}
/**
@@ -580,6 +585,14 @@ public class BrowserControlsManager implements ActivityStateListener, BrowserCon
}
}
+ @Override
+ public int getTopControlsHeightRealOffset() {
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled())
+ return 0;
+ else
+ return mTopControlsHeight;
+ }
+
@Override
public int getTopControlsMinHeight() {
return mTopControlsMinHeight;
@@ -681,6 +694,8 @@ public class BrowserControlsManager implements ActivityStateListener, BrowserCon
@Override
public float getTopVisibleContentOffset() {
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled())
+ return 0;
return getTopControlsHeight() + getTopControlOffset();
}
@@ -931,7 +946,10 @@ public class BrowserControlsManager implements ActivityStateListener, BrowserCon
TraceEvent.scoped("BrowserControlsManager.notifyControlOffsetChanged")) {
scheduleVisibilityUpdate();
if (shouldShowAndroidControls() && mControlsPosition == ControlsPosition.TOP) {
- mControlContainer.getView().setTranslationY(getTopControlOffset());
+ var topControlOffset = getTopControlOffset();
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled())
+ topControlOffset = -topControlOffset;
+ mControlContainer.getView().setTranslationY(topControlOffset);
}
// Explicitly tell the compositor to draw again. Should be |true| only when the android
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/messages/MessageContainerCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/messages/MessageContainerCoordinator.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/messages/MessageContainerCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/messages/MessageContainerCoordinator.java
@@ -18,6 +18,10 @@ import org.chromium.chrome.browser.fullscreen.BrowserControlsManager;
import org.chromium.components.messages.MessageContainer;
import org.chromium.ui.base.ViewUtils;
+import android.view.Gravity;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
+
/**
* Coordinator of {@link MessageContainer}, which can adjust margins of the message container and
* control the visibility of browser control when message is being shown.
@@ -60,9 +64,14 @@ public class MessageContainerCoordinator implements BrowserControlsStateProvider
if (mContainer.getVisibility() != View.VISIBLE) {
return;
}
- ViewGroup.MarginLayoutParams params =
- (ViewGroup.MarginLayoutParams) mContainer.getLayoutParams();
- params.topMargin = getContainerTopOffset();
+ CoordinatorLayout.LayoutParams params =
+ (CoordinatorLayout.LayoutParams) mContainer.getLayoutParams();
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ params.gravity = Gravity.START | Gravity.BOTTOM;
+ params.bottomMargin = getContainerTopOffset();
+ } else {
+ params.topMargin = getContainerTopOffset();
+ }
mContainer.setLayoutParams(params);
}
@@ -171,7 +180,10 @@ public class MessageContainerCoordinator implements BrowserControlsStateProvider
*/
private int getContainerTopOffset() {
assert mContainer != null;
-
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ return mControlsManager.getContentOffset()
+ + (mControlsManager.getBottomControlsHeight() - mControlsManager.getBottomControlOffset());
+ }
if (mControlsManager.getContentOffset() == 0) return 0;
final Resources res = mContainer.getResources();
return mControlsManager.getContentOffset()
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ChromeTabModalPresenter.java b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ChromeTabModalPresenter.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ChromeTabModalPresenter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ChromeTabModalPresenter.java
@@ -348,7 +348,7 @@ public class ChromeTabModalPresenter extends TabModalPresenter
* @return The container top margin.
*/
public static int getContainerTopMargin(BrowserControlsStateProvider provider) {
- return provider.getTopControlsHeight();
+ return provider.getTopControlsHeightRealOffset();
}
/**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -109,6 +109,7 @@ import org.chromium.ui.base.ActivityResultTracker;
import org.chromium.ui.base.DeviceFormFactor;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
import androidx.annotation.NonNull;
import org.chromium.chrome.browser.bookmarks.BookmarkManagerOpener;
@@ -761,11 +762,16 @@ public class NewTabPage
final int topControlsDistanceToRest =
mBrowserControlsStateProvider.getContentOffset()
- mBrowserControlsStateProvider.getTopControlsHeight();
- final int topMargin = getToolbarExtraYOffset() + topControlsDistanceToRest;
+ int topMargin = getToolbarExtraYOffset() + topControlsDistanceToRest;
- final int bottomMargin =
+ int bottomMargin =
mBrowserControlsStateProvider.getBottomControlsHeight()
- mBrowserControlsStateProvider.getBottomControlOffset();
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ // move the margin of the new tab page up if the top toolbar is at the bottom
+ bottomMargin += mBrowserControlsStateProvider.getTopControlsHeight();
+ topMargin = -mBrowserControlsStateProvider.getTopControlsHeight();
+ }
if (topMargin != layoutParams.topMargin || bottomMargin != layoutParams.bottomMargin) {
layoutParams.topMargin = topMargin;
@@ -781,9 +787,7 @@ public class NewTabPage
* strip.
*/
private int getToolbarExtraYOffset() {
- return mBrowserControlsStateProvider.getTopControlsHeight()
- - mToolbarHeight
- - mTabStripHeightSupplier.get();
+ return 0;
}
/** Returns the instance of {@link NewTabPageCoordinator}. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
@@ -16,6 +16,11 @@ import org.chromium.build.annotations.NullMarked;
import org.chromium.build.annotations.Nullable;
import org.chromium.chrome.R;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.FrameLayout;
+import android.view.Gravity;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+
/**
* Layout for the new tab page. This positions the page elements in the correct vertical positions.
* There are no separate phone and tablet UIs; this layout adapts based on the available space.
@@ -59,7 +64,12 @@ public class NewTabPageLayout extends LinearLayout {
Log.i(TAG, "NewTabPageLayout.onFinishInflate before insertSiteSectionView");
initializeSiteSectionView();
-
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ FrameLayout.LayoutParams params =
+ new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ params.gravity = Gravity.BOTTOM;
+ setLayoutParams(params);
+ }
Log.i(TAG, "NewTabPageLayout.onFinishInflate after insertSiteSectionView");
}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java
@@ -34,6 +34,7 @@ import org.chromium.components.embedder_support.util.UrlConstants;
import org.chromium.ui.base.DeviceFormFactor;
import org.chromium.ui.base.ViewUtils;
import org.chromium.ui.edge_to_edge.EdgeToEdgePadAdjuster;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
/**
* The native recent tabs page. Lists recently closed tabs, open windows and tabs from the user's
@@ -118,7 +119,8 @@ public class RecentTabsPage
mView.addOnAttachStateChangeListener(this);
- if (!DeviceFormFactor.isNonMultiDisplayContextOnTablet(mActivity)) {
+ if (!DeviceFormFactor.isNonMultiDisplayContextOnTablet(mActivity) ||
+ ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
mBrowserControlsStateProvider = browserControlsStateProvider;
mBrowserControlsStateProvider.addObserver(this);
onBottomControlsHeightChanged(
@@ -130,14 +132,23 @@ public class RecentTabsPage
mTabStripHeightSupplier = tabStripHeightSupplier;
mView.setPadding(0, mTabStripHeightSupplier.get(), 0, 0);
- mTabStripHeightChangeCallback =
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ mView.setPadding(
+ mView.getPaddingLeft(),
+ 0,
+ mView.getPaddingRight(),
+ mView.getPaddingBottom());
+ mTabStripHeightChangeCallback = newHeight -> { };
+ } else {
+ mTabStripHeightChangeCallback =
newHeight ->
mView.setPadding(
mView.getPaddingLeft(),
newHeight,
mView.getPaddingRight(),
mView.getPaddingBottom());
- mTabStripHeightSupplier.addSyncObserverAndPostIfNonNull(mTabStripHeightChangeCallback);
+ mTabStripHeightSupplier.addSyncObserverAndPostIfNonNull(mTabStripHeightChangeCallback);
+ }
mEdgeToEdgeSupplier = edgeToEdgeSupplier;
mPadAdjuster =
EdgeToEdgeControllerFactory.createForViewAndObserveSupplier(
@@ -363,7 +374,7 @@ public class RecentTabsPage
if (mBrowserControlsStateProvider == null) return;
final View recentTabsRoot = mView.findViewById(R.id.recent_tabs_root);
- final int topControlsHeight = mBrowserControlsStateProvider.getTopControlsHeight();
+ final int topControlsHeight = mBrowserControlsStateProvider.getTopControlsHeightRealOffset();
final int contentOffset = mBrowserControlsStateProvider.getContentOffset();
ViewGroup.MarginLayoutParams layoutParams =
(ViewGroup.MarginLayoutParams) recentTabsRoot.getLayoutParams();
@@ -379,9 +390,17 @@ public class RecentTabsPage
// If the content offset is different from the margin, we use translationY to position the
// view in line with the content offset.
- recentTabsRoot.setTranslationY(contentOffset - topMargin);
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ topMargin = 0;
+ recentTabsRoot.setTranslationY(0);
+ } else {
+ recentTabsRoot.setTranslationY(contentOffset - topMargin);
+ }
- final int bottomMargin = mBrowserControlsStateProvider.getBottomControlsHeight();
+ int bottomMargin = mBrowserControlsStateProvider.getBottomControlsHeight();
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ bottomMargin += mBrowserControlsStateProvider.getTopControlsHeight();
+ }
if (topMargin != layoutParams.topMargin || bottomMargin != layoutParams.bottomMargin) {
layoutParams.topMargin = topMargin;
layoutParams.bottomMargin = bottomMargin;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/page_info/ChromePageInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/page_info/ChromePageInfo.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/page_info/ChromePageInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/page_info/ChromePageInfo.java
@@ -14,6 +14,7 @@ import org.chromium.build.annotations.Nullable;
import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider.ControlsPosition;
import org.chromium.chrome.browser.ephemeraltab.EphemeralTabCoordinator;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.fullscreen.BrowserControlsManagerSupplier;
import org.chromium.chrome.browser.merchant_viewer.PageInfoStoreInfoController.StoreInfoActionHandler;
import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
@@ -91,6 +92,7 @@ public class ChromePageInfo {
if (stateProvider != null) {
dialogPosition =
stateProvider.getControlsPosition() == ControlsPosition.BOTTOM
+ || ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()
? Gravity.BOTTOM
: Gravity.TOP;
}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
@@ -92,6 +92,11 @@ import org.chromium.url.GURL;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+
+import android.view.Gravity;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
+
import java.lang.ref.WeakReference;
import java.util.function.Supplier;
@@ -311,6 +316,15 @@ public class SearchActivity extends AsyncInitializationActivity
// Build the search box.
mSearchBox = contentView.findViewById(R.id.search_location_bar);
mAnchorView = contentView.findViewById(R.id.toolbar);
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams)
+ mAnchorView.getLayoutParams();
+ layoutParams.gravity = Gravity.START | Gravity.BOTTOM;
+ mAnchorView.setLayoutParams(layoutParams);
+ View omniboxResultView = contentView.findViewById(R.id.omnibox_results_container_stub);
+ omniboxResultView.setTranslationY(
+ -getResources().getDimensionPixelSize(R.dimen.control_container_height_cromite));
+ }
// Update the status bar's color based on the toolbar color.
setStatusAndNavBarColors();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/FragmentDependencyProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/FragmentDependencyProvider.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/FragmentDependencyProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/FragmentDependencyProvider.java
@@ -220,6 +220,11 @@ public class FragmentDependencyProvider extends FragmentManager.FragmentLifecycl
if (fragment instanceof INeedSnackbarManager) {
((INeedSnackbarManager)fragment).setSnackbarManagerSupplier(mSnackbarManagerSupplier);
}
+ if (fragment instanceof AccessibilitySettings) {
+ ((ChromeAccessibilitySettingsDelegate)
+ ((AccessibilitySettings) fragment)
+ .getDelegate()).setSnackbarManagerSupplier(mSnackbarManagerSupplier);
+ }
if (fragment instanceof AccountManagementFragment) {
((AccountManagementFragment) fragment)
.setSnackbarManagerSupplier(mSnackbarManagerSupplier);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorCoordinator.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorCoordinator.java
@@ -32,6 +32,10 @@ import org.chromium.ui.resources.dynamics.ViewResourceAdapter;
import java.util.function.Supplier;
+import android.view.Gravity;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
+
/**
* The coordinator for a status indicator that is positioned below the status bar and is persistent.
* Typically used to relay status, e.g. indicate user is offline.
@@ -206,6 +210,11 @@ public class StatusIndicatorCoordinator implements TopControlLayer {
private void initialize() {
final ViewStub stub = mActivity.findViewById(R.id.status_indicator_stub);
final ViewResourceFrameLayout root = (ViewResourceFrameLayout) stub.inflate();
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ // status messages (such as the offline indicator) are docked at the bottom
+ CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams)root.getLayoutParams();
+ layoutParams.gravity = Gravity.START | Gravity.BOTTOM;
+ }
mResourceId = root.getId();
mSceneLayer.setResourceId(mResourceId);
mResourceAdapter = root.getResourceAdapter();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorSceneLayer.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorSceneLayer.java
@@ -15,6 +15,7 @@ import org.chromium.chrome.browser.layouts.SceneOverlay;
import org.chromium.chrome.browser.layouts.scene_layer.SceneLayer;
import org.chromium.chrome.browser.layouts.scene_layer.SceneOverlayLayer;
import org.chromium.ui.resources.ResourceManager;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
/**
* A composited view that is positioned below the status bar and is persistent. Typically used to
@@ -75,7 +76,10 @@ class StatusIndicatorSceneLayer extends SceneOverlayLayer implements SceneOverla
@Override
public SceneOverlayLayer getUpdatedSceneOverlayTree(
RectF viewport, RectF visibleViewport, ResourceManager resourceManager) {
- final int offset = mBrowserControlsStateProvider.getTopControlsMinHeightOffset();
+ int offset = mBrowserControlsStateProvider.getTopControlsMinHeightOffset();
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ offset = (int)viewport.bottom - offset;
+ }
StatusIndicatorSceneLayerJni.get()
.updateStatusIndicatorLayer(mNativePtr, resourceManager, mResourceId, offset);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -252,6 +252,9 @@ import org.chromium.url.GURL;
import java.util.List;
import java.util.function.Supplier;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import android.view.Gravity;
+
/**
* Contains logic for managing the toolbar visual component. This class manages the interactions
* with the rest of the application to ensure the toolbar is always visually up to date.
@@ -910,6 +913,17 @@ public class ToolbarManager
/* matchTrustedCdnUrl= */ mIsCustomTab);
mControlContainer = controlContainer;
mToolbarHairline = mControlContainer.findViewById(R.id.toolbar_hairline);
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ ViewGroup.MarginLayoutParams layoutParamsCC =
+ ((ViewGroup.MarginLayoutParams) mControlContainer.getLayoutParams());
+ layoutParamsCC.bottomMargin = mToolbarHairline.getHeight();
+ mControlContainer.setLayoutParams(layoutParamsCC);
+
+ ViewGroup.MarginLayoutParams layoutParamsHR =
+ ((ViewGroup.MarginLayoutParams) mToolbarHairline.getLayoutParams());
+ layoutParamsHR.topMargin = 0;
+ mToolbarHairline.setLayoutParams(layoutParamsHR);
+ }
mBookmarkModelSupplier = bookmarkModelSupplier;
mBookmarkModelSupplier.addSyncObserverAndPostIfNonNull(mBookmarkModelSupplierObserver);
@@ -1637,6 +1651,7 @@ public class ToolbarManager
// the height won't be measured by the background image.
if (mControlContainer.getBackground() == null) {
setControlContainerTopMargin(getToolbarExtraYOffset());
+ MoveBottomBarOverTopBar();
} else if (mLayoutChangeListener == null) {
mLayoutChangeListener =
(view,
@@ -1650,6 +1665,7 @@ public class ToolbarManager
oldBottom) -> {
if (mControlContainer.getBackground() == null) {
setControlContainerTopMargin(getToolbarExtraYOffset());
+ MoveBottomBarOverTopBar();
mControlContainer.removeOnLayoutChangeListener(
mLayoutChangeListener);
mLayoutChangeListener = null;
@@ -2310,6 +2326,17 @@ public class ToolbarManager
return ((LocationBarCoordinator) mLocationBar).getUrlBarViewRectProvider();
}
+ View mBottomRoot;
+
+ private void MoveBottomBarOverTopBar() {
+ if (mBottomRoot != null &&
+ ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ // move up the container view of the ui
+ // below there is the toolbar
+ mBottomRoot.setTranslationY(-mBrowserControlsSizer.getTopControlsHeight());
+ }
+ }
+
/** Enable the bottom controls. */
public void enableBottomControls() {
enableTabGroupUiControls();
@@ -2319,8 +2346,9 @@ public class ToolbarManager
}
private void enableTabGroupUiControls() {
- View tabGroupUiContainer =
+ mBottomRoot =
((ViewStub) mActivity.findViewById(R.id.tab_group_ui_container_stub)).inflate();
+ MoveBottomBarOverTopBar();
assert mTabGroupUiOneshotSupplier == null;
assert mUndoBarThrottle != null;
assert mLayoutManager != null;
@@ -2338,7 +2366,7 @@ public class ToolbarManager
mActivityTabProvider,
mTabModelSelector,
mActivity,
- tabGroupUiContainer.findViewById(R.id.bottom_container_slot),
+ mBottomRoot.findViewById(R.id.bottom_container_slot),
mBrowserControlsSizer,
mScrimManager,
mOmniboxFocusStateSupplier,
@@ -2365,7 +2393,7 @@ public class ToolbarManager
mControlsVisibilityDelegate,
mFullscreenManager,
mEdgeToEdgeControllerSupplier,
- (ScrollingBottomViewResourceFrameLayout) tabGroupUiContainer,
+ (ScrollingBottomViewResourceFrameLayout) mBottomRoot,
LayerType.TABSTRIP_TOOLBAR,
tabGroupUiBottomControlsContentDelegateSupplier,
mTabObscuringHandler,
@@ -2374,7 +2402,8 @@ public class ToolbarManager
/* readAloudRestoringSupplier= */ () -> {
final var readAloud = mReadAloudControllerSupplier.get();
return readAloud != null && readAloud.isRestoringPlayer();
- });
+ },
+ mTopUiThemeColorProvider, mActivityTabProvider.asObservable());
if (mInitializedWithNative) {
tabGroupUiBottomControlsCoordinator.initializeWithNative();
}
@@ -2422,7 +2451,7 @@ public class ToolbarManager
/* readAloudRestoringSupplier= */ () -> {
final var readAloud = mReadAloudControllerSupplier.get();
return readAloud != null && readAloud.isRestoringPlayer();
- });
+ }, mTopUiThemeColorProvider, mActivityTabProvider.asObservable());
if (mInitializedWithNative) {
bottomAppBarCoordinator.initializeWithNative();
}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomContainer.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomContainer.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomContainer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomContainer.java
@@ -23,6 +23,7 @@ import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider
import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeController;
import org.chromium.ui.base.ApplicationViewportInsetTracker;
import org.chromium.ui.base.ViewportInsets;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
/**
* The container that holds both infobars and snackbars. It will be translated up and down when the
@@ -80,11 +81,28 @@ public class BottomContainer extends FrameLayout
setTranslationY(mBaseYOffset);
}
+ @Override
+ public void onTopControlsHeightChanged(int topControlsHeight, int topControlsMinHeight) {
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled())
+ setTranslationY(mBaseYOffset);
+ }
+
+ @Override
+ public void onAndroidControlsVisibilityChanged(int visibility) {
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled())
+ setTranslationY(mBaseYOffset);
+ }
+
@Override
public void setTranslationY(float y) {
mBaseYOffset = y;
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ // the snackbar container is moved up because there is the top toolbar at the bottom
+ mBaseYOffset = -(mBrowserControlsStateProvider.getTopControlsHeight()
+ + mBrowserControlsStateProvider.getTopControlOffset());
+ }
float offsetFromControls =
mBrowserControlsStateProvider.getBottomControlOffset()
- mBrowserControlsStateProvider.getBottomControlsHeight();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java
@@ -15,6 +15,7 @@ import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider
import org.chromium.chrome.browser.browser_controls.BrowserControlsVisibilityManager;
import org.chromium.chrome.browser.compositor.overlay_panel.OverlayPanel;
import org.chromium.chrome.browser.compositor.overlay_panel.OverlayPanelManager;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.layouts.LayoutStateProvider;
import org.chromium.chrome.browser.layouts.LayoutStateProvider.LayoutStateObserver;
import org.chromium.chrome.browser.layouts.LayoutType;
@@ -166,9 +167,18 @@ class BottomSheetManager extends EmptyBottomSheetObserver implements DestroyObse
mBrowserControlsVisibilityManager.getBrowserControlHiddenRatio());
}
+ @Override
+ public void onTopControlsHeightChanged(int topControlsHeight, int topControlsMinHeight) {
+ onBottomControlsHeightChanged(controlsVisibilityManager.getBottomControlsHeight(),
+ controlsVisibilityManager.getBottomControlsMinHeight());
+ }
+
@Override
public void onBottomControlsHeightChanged(
int bottomControlsHeight, int bottomControlsMinHeight) {
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ bottomControlsHeight += controlsVisibilityManager.getTopControlsHeight();
+ }
mSheetController.setBottomControlsOffset(bottomControlsHeight);
}
};
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/system/StatusBarColorController.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/system/StatusBarColorController.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/system/StatusBarColorController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/system/StatusBarColorController.java
@@ -12,6 +12,7 @@ import android.graphics.Color;
import android.os.Build;
import android.view.View;
import android.view.Window;
+import android.os.Build;
import androidx.annotation.ColorInt;
import androidx.annotation.VisibleForTesting;
@@ -25,6 +26,7 @@ import org.chromium.build.annotations.NullMarked;
import org.chromium.build.annotations.Nullable;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ActivityTabProvider;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.layouts.LayoutManager;
import org.chromium.chrome.browser.layouts.LayoutStateProvider;
import org.chromium.chrome.browser.layouts.LayoutStateProvider.LayoutStateObserver;
@@ -623,6 +625,12 @@ public class StatusBarColorController
UiUtils.setStatusBarColor(window, color);
}
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled() &&
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ UiUtils.setNavigationBarIconColor(window.getDecorView().getRootView(),
+ needsDarkStatusBarIcons);
+ window.setNavigationBarColor(color);
+ }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
var taskDescription =
new ActivityManager.TaskDescription.Builder().setStatusBarColor(color).build();
diff --git a/chrome/browser/android/compositor/layer/toolbar_layer.cc b/chrome/browser/android/compositor/layer/toolbar_layer.cc
--- a/chrome/browser/android/compositor/layer/toolbar_layer.cc
+++ b/chrome/browser/android/compositor/layer/toolbar_layer.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/android/compositor/layer/toolbar_layer.h"
#include "base/feature_list.h"
+#include "cc/base/features.h"
#include "cc/resources/scoped_ui_resource.h"
#include "cc/slim/layer.h"
#include "cc/slim/nine_patch_layer.h"
@@ -153,7 +154,10 @@ void ToolbarLayer::PushResource(int toolbar_resource_id,
// always at the bottom of the browser controls. This is no longer the case
// as for 2025.
// TODO(https://crbug.com/454338286): Rename / remove in favor of y_Offset.
- if (!base::FeatureList::IsEnabled(chrome::android::kTopControlsRefactor) ||
+ if (base::FeatureList::IsEnabled(::features::kMoveTopToolbarToBottom)) {
+ y_offset = y_offset + legacy_content_offset - layer_->bounds().height() - resource->shadow_height();
+ }
+ else if (!base::FeatureList::IsEnabled(chrome::android::kTopControlsRefactor) ||
!base::FeatureList::IsEnabled(chrome::android::kTopControlsRefactorV2) ||
kInvalidContentOffset != legacy_content_offset) {
y_offset = legacy_content_offset - layer_->bounds().height();
diff --git a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc
--- a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc
+++ b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc
@@ -18,6 +18,7 @@
#include "ui/base/l10n/l10n_util_android.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/transform.h"
+#include "cc/base/features.h"
// Must come after all headers that specialize FromJniType() / ToJniType().
#include "chrome/android/chrome_jni_headers/TabStripSceneLayer_jni.h"
@@ -166,8 +167,10 @@ void TabStripSceneLayer::SetContentTree(JNIEnv* env,
content_tree_ = content_tree;
if (content_tree) {
layer()->InsertChild(content_tree->layer(), 0);
- content_tree->layer()->SetPosition(
- gfx::PointF(0, -layer()->position().y()));
+ if (!base::FeatureList::IsEnabled(::features::kMoveTopToolbarToBottom)) {
+ content_tree->layer()->SetPosition(
+ gfx::PointF(0, -layer()->position().y()));
+ }
}
}
}
@@ -222,7 +225,12 @@ void TabStripSceneLayer::UpdateTabStripLayer(JNIEnv* env,
float right_padding,
float top_padding) {
gfx::RectF content(0, y_offset, width, height);
- layer()->SetPosition(gfx::PointF(0, y_offset));
+ if (base::FeatureList::IsEnabled(::features::kMoveTopToolbarToBottom)) {
+ // do not move the whole layer (which also contains the contents) but only the tab strip layer
+ background_layer_->SetPosition(gfx::PointF(0, y_offset));
+ } else {
+ layer()->SetPosition(gfx::PointF(0, y_offset));
+ }
background_layer_->SetBounds(gfx::Size(width, height));
background_layer_->SetBackgroundColor(SkColor4f::FromColor(background_color));
@@ -231,7 +239,7 @@ void TabStripSceneLayer::UpdateTabStripLayer(JNIEnv* env,
tab_strip_layer_->SetPosition(gfx::PointF(0, top_padding));
// Content tree should not be affected by tab strip scene layer visibility.
- if (content_tree_) {
+ if (content_tree_ && !base::FeatureList::IsEnabled(::features::kMoveTopToolbarToBottom)) {
content_tree_->layer()->SetPosition(gfx::PointF(0, -y_offset));
}
diff --git a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BottomControlsStacker.java b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BottomControlsStacker.java
--- a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BottomControlsStacker.java
+++ b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BottomControlsStacker.java
@@ -283,7 +283,7 @@ public class BottomControlsStacker implements BrowserControlsStateProvider.Obser
/**
* @return {@link BrowserControlsStateProvider} instance in the current Activity.
*/
- public BrowserControlsStateProvider getBrowserControls() {
+ public BrowserControlsSizer getBrowserControls() {
return mBrowserControlsSizer;
}
diff --git a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsMarginAdapter.java b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsMarginAdapter.java
--- a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsMarginAdapter.java
+++ b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsMarginAdapter.java
@@ -10,6 +10,7 @@ import org.chromium.base.lifetime.Destroyable;
import org.chromium.base.supplier.MonotonicObservableSupplier;
import org.chromium.base.supplier.SettableMonotonicObservableSupplier;
import org.chromium.build.annotations.NullMarked;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
/**
* An implementation of {@link MonotonicObservableSupplier} that monitors changes to browser
@@ -75,6 +76,10 @@ public class BrowserControlsMarginAdapter
int bottomMargin =
mBrowserControlsStateProvider.getBottomControlsHeight()
- mBrowserControlsStateProvider.getBottomControlOffset();
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ bottomMargin += topMargin;
+ topMargin = 0;
+ }
mTargetSupplier.set(new Rect(0, topMargin, 0, bottomMargin));
}
}
diff --git a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsStateProvider.java b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsStateProvider.java
--- a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsStateProvider.java
+++ b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsStateProvider.java
@@ -128,6 +128,12 @@ public interface BrowserControlsStateProvider {
*/
int getTopControlsHairlineHeight();
+ /**
+ * @return The height of the top controls in pixels.
+ * returns 0 is the toolbar is at the bottom
+ */
+ int getTopControlsHeightRealOffset();
+
/**
* @return The minimum visible height top controls can have in pixels.
*/
diff --git a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsUtils.java b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsUtils.java
--- a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsUtils.java
+++ b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsUtils.java
@@ -83,7 +83,7 @@ public class BrowserControlsUtils {
* @return Whether the browser controls should be drawn as a texture.
*/
public static boolean drawControlsAsTexture(BrowserControlsStateProvider stateProvider) {
- return stateProvider.getBrowserControlHiddenRatio() > 0;
+ return false;
}
/**
diff --git a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/TopControlsStacker.java b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/TopControlsStacker.java
--- a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/TopControlsStacker.java
+++ b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/TopControlsStacker.java
@@ -549,7 +549,7 @@ public class TopControlsStacker implements BrowserControlsStateProvider.Observer
// mLayerYOffsets has the visually accurate offsets. This is needed so we can handle
// offset updates due to constraint changes.
if (!offsetsAppliedByBrowser) {
- yOffset = mLayerRestingOffsets.get(type);
+ //yOffset = mLayerRestingOffsets.get(type);
}
}
layer.onBrowserControlsOffsetUpdate(yOffset, controlsAtResting);
@@ -606,8 +606,8 @@ public class TopControlsStacker implements BrowserControlsStateProvider.Observer
// To avoid scrollable layers keeps getting update after it is scrolled off,
// limit the yOffset, so the scrollable layer's bottom is aligned with
// the bottom of the last non-scrollable layer (nonScrollableYOffset).
- int optimizedYOffset =
- Math.max(scrollableYOffset, nonScrollableYOffset - layerHeight);
+ int optimizedYOffset = scrollableYOffset;
+ //Math.max(scrollableYOffset, nonScrollableYOffset - layerHeight);
yOffsetOfLayers.put(type, optimizedYOffset);
scrollableYOffset += layerHeight;
}
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedListContentManager.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedListContentManager.java
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedListContentManager.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedListContentManager.java
@@ -15,6 +15,7 @@ import androidx.annotation.Px;
import org.chromium.build.annotations.NullMarked;
import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.xsurface.ListContentManager;
import org.chromium.chrome.browser.xsurface.ListContentManagerObserver;
import org.chromium.chrome.browser.xsurface.LoggingParameters;
@@ -144,7 +145,10 @@ public class FeedListContentManager implements ListContentManager {
FrameLayout enclosingLayout = new FrameLayout(parent.getContext());
FrameLayout.LayoutParams layoutParams =
new FrameLayout.LayoutParams(
- new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ new LayoutParams(LayoutParams.MATCH_PARENT,
+ ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()
+ ? LayoutParams.MATCH_PARENT
+ : LayoutParams.WRAP_CONTENT));
enclosingLayout.setLayoutParams(layoutParams);
// Set the left and right paddings.
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -13,6 +13,7 @@
#include "base/features.h"
#include "base/no_destructor.h"
#include "build/android_buildflags.h"
+#include "cc/base/features.h"
#include "chrome/browser/android/webapk/webapk_features.h"
#include "chrome/browser/browser_features.h"
#include "chrome/browser/contextmenu/context_menu_features.h"
@@ -403,6 +404,8 @@ const base::Feature* const kFeaturesExposedToJava[] = {
&kNewTabPageCustomizationForMvt,
&kNewTabPageCustomizationThemeSync,
&kNewTabPageCustomizationV2,
+ &features::kMoveTopToolbarToBottom,
+ &features::kDisableToolbarSwipeUp,
&kNotificationPermissionVariant,
&kNotificationTrampoline,
&kNotificationTrampolineNoNewTask,
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -542,6 +542,10 @@ public abstract class ChromeFeatureList {
public static final String MVC_UPDATE_VIEW_WHEN_MODEL_CHANGED = "MvcUpdateViewWhenModelChanged";
public static final String NAV_BAR_COLOR_ANIMATION = "NavBarColorAnimation";
public static final String NEW_TAB_PAGE_CUSTOMIZATION_FOR_MVT = "NewTabPageCustomizationForMvt";
+ public static final String DISABLE_TOOLBAR_SWIPE_UP =
+ "DisableToolbarSwipeUp";
+ public static final String MOVE_TOP_TOOLBAR_TO_BOTTOM =
+ "MoveTopToolbarToBottom";
public static final String NEW_TAB_PAGE_CUSTOMIZATION_THEME_SYNC =
"NewTabPageCustomizationThemeSync";
public static final String NEW_TAB_PAGE_CUSTOMIZATION_V2 = "NewTabPageCustomizationV2";
@@ -1099,6 +1103,10 @@ public abstract class ChromeFeatureList {
public static final CachedFlag sNtpSimplification =
newCachedFlag(NTP_SIMPLIFICATION, /* defaultValue= */ true);
public static final CachedFlag sPCctMinimumHeight = newCachedFlag(PCCT_MINIMUM_HEIGHT, true);
+ public static final CachedFlag sDisableToolbarSwipeUp =
+ newCachedFlag(DISABLE_TOOLBAR_SWIPE_UP, false);
+ public static final CachedFlag sMoveTopToolbarToBottom =
+ newCachedFlag(MOVE_TOP_TOOLBAR_TO_BOTTOM, false);
public static final CachedFlag sPaintPreviewDemo = newCachedFlag(PAINT_PREVIEW_DEMO, false);
public static final CachedFlag sPersistAcrossReboots =
newCachedFlag(PERSIST_ACROSS_REBOOTS, true);
@@ -1356,6 +1364,8 @@ public abstract class ChromeFeatureList {
sNtpMvcRefactor,
sNtpSimplification,
sPCctMinimumHeight,
+ sMoveTopToolbarToBottom,
+ sDisableToolbarSwipeUp,
sPaintPreviewDemo,
sPersistAcrossReboots,
sPersistAcrossRebootsDebugLogs,
diff --git a/chrome/browser/hub/internal/BUILD.gn b/chrome/browser/hub/internal/BUILD.gn
--- a/chrome/browser/hub/internal/BUILD.gn
+++ b/chrome/browser/hub/internal/BUILD.gn
@@ -17,6 +17,7 @@ android_library("java") {
"//chrome/test/android:chrome_java_transit",
]
sources = [
+ "android/java/src/org/chromium/chrome/browser/hub/ReversedLinearLayout.java",
"android/java/src/org/chromium/chrome/browser/hub/ApplyButtonData.java",
"android/java/src/org/chromium/chrome/browser/hub/EmptyHubBottomToolbarDelegate.java",
"android/java/src/org/chromium/chrome/browser/hub/FadeHubLayoutAnimationFactory.java",
@@ -107,6 +108,10 @@ android_library("java") {
"//ui/edge_to_edge:java",
"//url:url_java",
]
+ deps += [
+ "//base:base_cached_flags_java",
+ "//chrome/browser/flags:java",
+ ]
}
android_resources("java_resources") {
diff --git a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubCoordinator.java b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubCoordinator.java
--- a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubCoordinator.java
+++ b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubCoordinator.java
@@ -14,6 +14,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import android.widget.LinearLayout;
import org.chromium.base.Callback;
import org.chromium.base.DeviceInfo;
@@ -29,6 +30,7 @@ import org.chromium.build.annotations.NullMarked;
import org.chromium.build.annotations.Nullable;
import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
import org.chromium.chrome.browser.hub.HubPaneHostView.OnPaneSwipeListener;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.profiles.ProfileProvider;
import org.chromium.chrome.browser.tab.Tab;
@@ -180,6 +182,13 @@ public class HubCoordinator implements PaneHubController, BackPressHandler, OnPa
paneManager.getFocusedPaneSupplier(),
hubColorMixer,
defaultPaneId);
+ if (!ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ LinearLayout mainContainer = mContainerView.findViewById(R.id.hub_pane_host_container);
+ LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)
+ mainContainer.getLayoutParams();
+ params.topMargin = 0;
+ mainContainer.setLayoutParams(params);
+ }
NullableObservableSupplier<View> overlayViewSupplier =
mPaneManager
diff --git a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubManagerImpl.java b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubManagerImpl.java
--- a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubManagerImpl.java
+++ b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubManagerImpl.java
@@ -8,6 +8,7 @@ import static org.chromium.build.NullUtil.assumeNonNull;
import android.app.Activity;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.FrameLayout.LayoutParams;
import androidx.annotation.ColorInt;
@@ -23,6 +24,7 @@ import org.chromium.build.annotations.EnsuresNonNull;
import org.chromium.build.annotations.MonotonicNonNull;
import org.chromium.build.annotations.NullMarked;
import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.back_press.BackPressManager;
import org.chromium.chrome.browser.profiles.ProfileProvider;
import org.chromium.chrome.browser.tab.Tab;
@@ -171,7 +173,10 @@ public class HubManagerImpl implements HubManager, HubController {
LayoutParams params = (LayoutParams) mHubContainerView.getLayoutParams();
assert params != null : "HubContainerView should always have layout params.";
mStatusIndicatorHeight = height;
- params.topMargin = mStatusIndicatorHeight + mAppHeaderHeight;
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled())
+ params.bottomMargin = mStatusIndicatorHeight + mAppHeaderHeight;
+ else
+ params.topMargin = mStatusIndicatorHeight + mAppHeaderHeight;
mHubContainerView.setLayoutParams(params);
}
@@ -181,7 +186,10 @@ public class HubManagerImpl implements HubManager, HubController {
LayoutParams params = (LayoutParams) mHubContainerView.getLayoutParams();
assert params != null : "HubContainerView should always have layout params.";
mAppHeaderHeight = height;
- params.topMargin = mStatusIndicatorHeight + mAppHeaderHeight;
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled())
+ params.bottomMargin = mStatusIndicatorHeight + mAppHeaderHeight;
+ else
+ params.topMargin = mStatusIndicatorHeight + mAppHeaderHeight;
mHubContainerView.setLayoutParams(params);
}
@@ -342,7 +350,9 @@ public class HubManagerImpl implements HubManager, HubController {
mHasSnackbarOverride = true;
mSnackbarManager.pushParentViewOverride(
ParentOverrideSlot.HUB,
- mHubCoordinator.getSnackbarContainer(),
+ ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()
+ ? (ViewGroup)getPaneHostView()
+ : mHubCoordinator.getSnackbarContainer(),
hasBottomToolbar ? mSnackbarMarginSupplier : null);
}
}
diff --git a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/ReversedLinearLayout.java b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/ReversedLinearLayout.java
new file mode 100644
--- /dev/null
+++ b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/ReversedLinearLayout.java
@@ -0,0 +1,42 @@
+package org.chromium.chrome.browser.hub;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+import android.view.Gravity;
+import android.view.View;
+
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+
+public class ReversedLinearLayout extends LinearLayout {
+ private boolean mReversedLayout;
+
+ public ReversedLinearLayout(Context context) {
+ super(context);
+ setReversedLayout();
+ }
+
+ public ReversedLinearLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setReversedLayout();
+ }
+
+ public ReversedLinearLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ setReversedLayout();
+ }
+
+ private void setReversedLayout() {
+ mReversedLayout = ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled();
+ if (mReversedLayout)
+ setGravity(Gravity.START | Gravity.BOTTOM);
+ }
+
+ @Override
+ public View getChildAt(int i) {
+ if (mReversedLayout)
+ return super.getChildAt(getChildCount() - 1 - i);
+ else
+ return super.getChildAt(i);
+ }
+}
diff --git a/chrome/browser/hub/internal/android/res/layout/hub_layout.xml b/chrome/browser/hub/internal/android/res/layout/hub_layout.xml
--- a/chrome/browser/hub/internal/android/res/layout/hub_layout.xml
+++ b/chrome/browser/hub/internal/android/res/layout/hub_layout.xml
@@ -8,11 +8,14 @@ found in the LICENSE file.
android:layout_width="match_parent"
android:layout_height="match_parent">
- <FrameLayout
+ <org.chromium.chrome.browser.hub.ReversedLinearLayout
android:id="@+id/hub_main_container"
+ android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <include layout="@layout/hub_toolbar_layout" />
+
<LinearLayout
android:id="@+id/hub_pane_host_container"
android:layout_width="match_parent"
@@ -26,9 +29,7 @@ found in the LICENSE file.
android:layout_weight="1" />
</LinearLayout>
- <include layout="@layout/hub_toolbar_layout" />
-
- </FrameLayout>
+ </org.chromium.chrome.browser.hub.ReversedLinearLayout>
<!-- z-index above everything else. -->
<include layout="@layout/hub_overlay_container" />
diff --git a/chrome/browser/hub/internal/android/res/layout/hub_toolbar_layout.xml b/chrome/browser/hub/internal/android/res/layout/hub_toolbar_layout.xml
--- a/chrome/browser/hub/internal/android/res/layout/hub_toolbar_layout.xml
+++ b/chrome/browser/hub/internal/android/res/layout/hub_toolbar_layout.xml
@@ -9,7 +9,7 @@ found in the LICENSE file.
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="@dimen/toolbar_height_no_shadow"
android:orientation="vertical"
tools:ignore="MergeRootFrame">
<!-- Should only be visible during animations involving hub search. -->
@@ -24,7 +24,7 @@ found in the LICENSE file.
<org.chromium.chrome.browser.hub.HubToolbarView
android:id="@+id/hub_toolbar"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="@dimen/toolbar_height_no_shadow"
android:orientation="vertical">
<FrameLayout
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -2128,7 +2128,7 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
prefs::kAppRatingPromptShown, false,
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
registry->RegisterBooleanPref(prefs::kVirtualKeyboardResizesLayoutByDefault,
- false);
+ true);
#endif
#if BUILDFLAG(ENTERPRISE_DATA_CONTROLS)
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java
@@ -14,6 +14,7 @@ import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.SystemClock;
import android.view.Gravity;
import android.view.KeyEvent;
@@ -44,6 +45,7 @@ import org.chromium.build.annotations.NullMarked;
import org.chromium.build.annotations.Nullable;
import org.chromium.build.annotations.RequiresNonNull;
import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider.ControlsPosition;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.ui.appmenu.internal.R;
import org.chromium.components.browser_ui.styles.SemanticColorUtils;
import org.chromium.components.browser_ui.widget.chips.ChipView;
@@ -428,6 +430,13 @@ class AppMenu implements OnKeyListener {
mListView.setVerticalScrollBarEnabled(false);
}
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ // always scroll to the bottom to show new items
+ mListView.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
+ // fill content starting from the bottom of the view
+ mListView.setStackFromBottom(true);
+ }
+
int footerHeight = attachFooter(footer, (ViewGroup) contentView, menuWidth);
int headerHeight = attachHeader(header, menuWidth);
@@ -475,7 +484,8 @@ class AppMenu implements OnKeyListener {
anchorView,
anchorViewOffset);
- popup.setHeight(calculateMenuHeight());
+ int popupHeight = calculateMenuHeight();
+ popup.setHeight(popupHeight);
int[] popupPosition =
getPopupPosition(
@@ -487,8 +497,14 @@ class AppMenu implements OnKeyListener {
padding,
anchorView,
popupWidth,
- anchorView.getRootView().getLayoutDirection());
+ anchorView.getRootView().getLayoutDirection(),
+ popupHeight);
popup.setContentView(contentView);
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ // due to some unknown behaviour, the popup must be resized to
+ // allow selection without leaving touch
+ popup.setHeight(popupHeight - 1);
+ }
mHierarchicalMenuController.setupFlyoutController(
/* flyoutHandler= */ flyoutHandler,
@@ -618,11 +634,20 @@ class AppMenu implements OnKeyListener {
Rect padding,
View anchorView,
int popupWidth,
- int viewLayoutDirection) {
+ int viewLayoutDirection,
+ int popupHeight) {
anchorView.getLocationInWindow(tempLocation);
int anchorViewX = tempLocation[0];
int anchorViewY = tempLocation[1];
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ // moves the view offset up by the height of the popup
+ anchorViewY -= popupHeight;
+ // fix it if it goes offscreen
+ if (anchorViewY <= negativeSoftwareVerticalOffset)
+ anchorViewY = negativeSoftwareVerticalOffset;
+ }
+
int[] offsets = new int[2];
// If we have a hardware menu button, locate the app menu closer to the estimated
// hardware menu button location.
@@ -772,7 +797,13 @@ class AppMenu implements OnKeyListener {
- mMenuSpec.footerHeight
- mMenuSpec.headerHeight
- anchorViewImpactHeight;
-
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N) {
+ // due to an Android Nougat bug the popup does not appear above the anchorview.
+ // the display is not pleasant, so we reduce the space
+ availableScreenSpace -= mMenuSpec.anchorView.getHeight();
+ }
+ }
if (mIsByPermanentButton) availableScreenSpace -= mMenuSpec.padding.top;
if (availableScreenSpace <= 0 && sExceptionReporter != null) {
String logMessage =
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java
@@ -29,6 +29,7 @@ import androidx.core.content.ContextCompat;
import org.chromium.base.Callback;
import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.build.annotations.MonotonicNonNull;
import org.chromium.build.annotations.NullMarked;
import org.chromium.build.annotations.Nullable;
@@ -311,7 +312,16 @@ class AppMenuHandlerImpl
assert !(isByPermanentButton && startDragging);
mModelList = mDelegate.getMenuItems();
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ // reverses the order of items in the menu
+ ModelList modelListReversed = new ModelList();
+ for (int i = 0; i < mModelList.size(); i++) {
+ modelListReversed.add(0, mModelList.get(i));
+ }
+ mModelList = modelListReversed;
+ }
mModelList.addObserver(mListObserver);
+
ContextThemeWrapper wrapper =
new ContextThemeWrapper(mContext, R.style.AppMenuThemeOverlay);
diff --git a/chrome/browser/ui/android/edge_to_edge/internal/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeControllerImpl.java b/chrome/browser/ui/android/edge_to_edge/internal/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeControllerImpl.java
--- a/chrome/browser/ui/android/edge_to_edge/internal/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeControllerImpl.java
+++ b/chrome/browser/ui/android/edge_to_edge/internal/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeControllerImpl.java
@@ -688,6 +688,7 @@ public class EdgeToEdgeControllerImpl
* the system insets due to the keyboard or the bottom controls being visible.
*/
private boolean shouldPadAdjusters() {
+ if ((true)) return false;
// Never pad the adjusters if the keyboard is visible.
if (mKeyboardInsets != null && mKeyboardInsets.bottom > 0) return false;
diff --git a/chrome/browser/ui/android/edge_to_edge/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeUtils.java b/chrome/browser/ui/android/edge_to_edge/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeUtils.java
--- a/chrome/browser/ui/android/edge_to_edge/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeUtils.java
+++ b/chrome/browser/ui/android/edge_to_edge/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeUtils.java
@@ -110,6 +110,7 @@ public class EdgeToEdgeUtils {
* being attached to the window.
*/
public static boolean isEdgeToEdgeBottomChinEnabled(Activity activity) {
+ if ((true)) return false;
// Make sure we test SDK version before checking the Feature so Field Trials only collect
// from qualifying devices.
if (!EdgeToEdgeFieldTrialImpl.getBottomChinOverrides().isEnabledForManufacturerVersion()) {
@@ -366,6 +367,7 @@ public class EdgeToEdgeUtils {
*/
@Deprecated
static boolean hasTappableNavigationBar(Window window) {
+ if ((true)) return true;
Supplier<WindowInsetsCompat> insetsSupplier =
() -> {
var rootInsets = window.getDecorView().getRootWindowInsets();
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/DeferredIMEWindowInsetApplicationCallback.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/DeferredIMEWindowInsetApplicationCallback.java
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/DeferredIMEWindowInsetApplicationCallback.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/DeferredIMEWindowInsetApplicationCallback.java
@@ -34,6 +34,7 @@ public class DeferredIMEWindowInsetApplicationCallback
private static final int NO_DEFERRED_KEYBOARD_HEIGHT = -1;
private int mDeferredKeyboardHeight = NO_DEFERRED_KEYBOARD_HEIGHT;
private int mKeyboardHeight;
+ private int mSystemBarInsetsBottom;
private boolean mAnimationInProgress;
private @Nullable WindowInsetsAnimationCompat mCurrentAnimation;
private @Nullable InsetObserver mInsetObserver;
@@ -89,6 +90,10 @@ public class DeferredIMEWindowInsetApplicationCallback
return mKeyboardHeight;
}
+ public int getSystemBarInsetsBottom() {
+ return mSystemBarInsetsBottom;
+ }
+
@Override
public void onPrepare(WindowInsetsAnimationCompat animation) {
if ((animation.getTypeMask() & WindowInsetsCompat.Type.ime()) == 0) return;
@@ -121,11 +126,13 @@ public class DeferredIMEWindowInsetApplicationCallback
public WindowInsetsCompat onApplyWindowInsets(
View view, WindowInsetsCompat windowInsetsCompat) {
int newKeyboardHeight = 0;
+ mSystemBarInsetsBottom = 0;
Insets imeInsets = windowInsetsCompat.getInsets(WindowInsetsCompat.Type.ime());
if (imeInsets.bottom > 0) {
Insets systemBarInsets =
windowInsetsCompat.getInsets(WindowInsetsCompat.Type.systemBars());
newKeyboardHeight = imeInsets.bottom - systemBarInsets.bottom;
+ mSystemBarInsetsBottom = systemBarInsets.bottom;
// Since the ime insets are greater than 0, the keyboard is showing, but its height is
// being suppressed in that this class deliberately wants to avoid application resizing.
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
@@ -282,6 +282,7 @@ public class LocationBarCoordinator
? ControlsPosition.TOP
: mBrowserControlsStateProvider.getControlsPosition(),
mDeferredIMEWindowInsetApplicationCallback::getCurrentKeyboardHeight,
+ mDeferredIMEWindowInsetApplicationCallback::getSystemBarInsetsBottom,
bottomWindowPaddingSupplier,
fuseboxStateSupplier,
locationBarDataProvider,
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxSuggestionsDropdownEmbedderImpl.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxSuggestionsDropdownEmbedderImpl.java
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxSuggestionsDropdownEmbedderImpl.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxSuggestionsDropdownEmbedderImpl.java
@@ -16,6 +16,7 @@ import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowInsets;
import androidx.core.graphics.Insets;
+import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import org.chromium.base.Callback;
@@ -55,6 +56,7 @@ class OmniboxSuggestionsDropdownEmbedderImpl
private final boolean mForcePhoneStyleOmnibox;
private final Supplier<@ControlsPosition Integer> mControlsPositionSupplier;
private final Supplier<Integer> mKeyboardHeightSupplier;
+ private final Supplier<Integer> mSystemBarInsetsBottomSupplier;
private final Supplier<Integer> mBottomWindowPaddingSupplier;
private final Context mContext;
private final TopInsetProvider mTopInsetProvider;
@@ -102,6 +104,7 @@ class OmniboxSuggestionsDropdownEmbedderImpl
@Nullable View baseChromeLayout,
Supplier<@ControlsPosition Integer> controlsPositionSupplier,
Supplier<Integer> keyboardHeightSupplier,
+ Supplier<Integer> systemBarInsetsBottomSupplier,
Supplier<Integer> bottomWindowPaddingSupplier,
Supplier<Integer> fuseboxStateSupplier,
LocationBarDataProvider locationBarDataProvider,
@@ -112,6 +115,7 @@ class OmniboxSuggestionsDropdownEmbedderImpl
mForcePhoneStyleOmnibox = forcePhoneStyleOmnibox;
mControlsPositionSupplier = controlsPositionSupplier;
mKeyboardHeightSupplier = keyboardHeightSupplier;
+ mSystemBarInsetsBottomSupplier = systemBarInsetsBottomSupplier;
mBottomWindowPaddingSupplier = bottomWindowPaddingSupplier;
mContext = mAnchorView.getContext();
mContext.registerComponentCallbacks(this);
@@ -139,6 +143,11 @@ class OmniboxSuggestionsDropdownEmbedderImpl
mOmniboxAlignmentSupplier.removeObserver(obs);
}
+ @Override
+ public View getAnchorView() {
+ return mAnchorView;
+ }
+
@Override
public OmniboxAlignment getCurrentAlignment() {
return assertNonNull(mOmniboxAlignmentSupplier.get());
@@ -170,6 +179,10 @@ class OmniboxSuggestionsDropdownEmbedderImpl
@Override
public void onDetachedFromWindow() {
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ recalculateOmniboxAlignment();
+ detachKeyboardAlignment();
+ }
mAnchorView.removeOnLayoutChangeListener(this);
mAlignmentView.removeOnLayoutChangeListener(this);
mAnchorView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
@@ -278,6 +291,9 @@ class OmniboxSuggestionsDropdownEmbedderImpl
} else {
ViewUtils.getRelativeLayoutPosition(contentView, mAnchorView, mPositionArray);
top = mPositionArray[1] + mAnchorView.getMeasuredHeight() - contentView.getPaddingTop();
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ top -= mPositionArray[1];
+ }
}
if (isTablet()) {
@@ -371,6 +387,17 @@ class OmniboxSuggestionsDropdownEmbedderImpl
height = Math.min(windowSpace, contentSpace) - top;
}
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ top = 0;
+ if (keyboardHeight > 0) {
+ ViewCompat.setPaddingRelative(
+ contentView, 0, 0, 0, keyboardHeight + mSystemBarInsetsBottomSupplier.get());
+ height -= mSystemBarInsetsBottomSupplier.get();
+ } else {
+ detachKeyboardAlignment();
+ }
+ }
+
// TODO(pnoland@, https://crbug.com/1416985): avoid pushing changes that are identical to
// the previous alignment value.
OmniboxAlignment omniboxAlignment =
@@ -413,6 +440,14 @@ class OmniboxSuggestionsDropdownEmbedderImpl
return result;
}
+ private void detachKeyboardAlignment() {
+ View contentView = mBaseChromeLayout;
+ if (contentView == null) {
+ contentView = mAnchorView.getRootView().findViewById(android.R.id.content);
+ }
+ ViewCompat.setPaddingRelative(contentView, 0, 0, 0, 0);
+ }
+
/**
* Returns whether the given view's position in the window has changed since the last call to
* offsetInWindowChanged().
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
@@ -14,6 +14,7 @@ import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
+import androidx.annotation.NonNull;
import androidx.annotation.ColorInt;
import androidx.annotation.VisibleForTesting;
import androidx.core.view.ViewCompat;
@@ -54,6 +55,7 @@ import org.chromium.ui.modaldialog.ModalDialogManager;
import org.chromium.ui.modelutil.LazyConstructionPropertyMcp;
import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
import java.util.ArrayList;
import java.util.List;
@@ -73,6 +75,7 @@ public class AutocompleteCoordinator implements OmniboxSuggestionsVisualState {
private @Nullable OmniboxSuggestionsDropdown mDropdown;
private final ObserverList<OmniboxSuggestionsDropdownScrollListener> mScrollListenerList =
new ObserverList<>();
+ private final @NonNull OmniboxSuggestionsDropdownEmbedder mDropdownEmbedder;
/** An observer watching for changes to the visual state of the omnibox suggestions. */
public interface OmniboxSuggestionsVisualStateObserver {
@@ -107,6 +110,7 @@ public class AutocompleteCoordinator implements OmniboxSuggestionsVisualState {
Context context = parent.getContext();
ModelList listItems = new ModelList();
+ mDropdownEmbedder = dropdownEmbedder;
PropertyModel listModel =
new PropertyModel.Builder(SuggestionListProperties.ALL_KEYS)
.with(SuggestionListProperties.EMBEDDER, dropdownEmbedder)
@@ -232,6 +236,18 @@ public class AutocompleteCoordinator implements OmniboxSuggestionsVisualState {
container.findViewById(R.id.omnibox_suggestions_dropdown);
dropdown.setAdapter(mAdapter);
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ // make margins works
+ dropdown.getViewGroup().setClipToPadding(true);
+ container.bringToFront();
+
+ // do not cover the bar
+ ViewGroup.LayoutParams params = container.getLayoutParams();
+ ((ViewGroup.MarginLayoutParams) params).topMargin = 0;
+ ((ViewGroup.MarginLayoutParams) params).bottomMargin =
+ mDropdownEmbedder.getAnchorView().getMeasuredHeight();
+ container.setLayoutParams(params);
+ }
if (mRecycledViewPool != null) {
dropdown.setRecycledViewPool(mRecycledViewPool);
}
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -87,6 +87,8 @@ import org.chromium.ui.modelutil.PropertyModelAnimatorFactory;
import org.chromium.ui.mojom.WindowOpenDisposition;
import org.chromium.url.GURL;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+
import java.util.List;
import java.util.function.Supplier;
@@ -1635,7 +1637,9 @@ class AutocompleteMediator
public void onSuggestionDropdownScroll() {
if (mAutocompleteInput == null) return;
mAutocompleteInput.setSuggestionsListScrolled();
- mDelegate.setKeyboardVisibility(false, false);
+ if (!ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ mDelegate.setKeyboardVisibility(false, false);
+ }
}
/**
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdown.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdown.java
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdown.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdown.java
@@ -300,8 +300,12 @@ public class OmniboxSuggestionsDropdown extends RecyclerView {
final Resources resources = context.getResources();
mBaseBottomPadding =
resources.getDimensionPixelOffset(R.dimen.omnibox_suggestion_list_padding_bottom);
- mBaseTopPadding =
- resources.getDimensionPixelOffset(R.dimen.omnibox_suggestion_list_padding_top);
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ mBaseTopPadding = 0;
+ } else {
+ mBaseTopPadding =
+ resources.getDimensionPixelOffset(R.dimen.omnibox_suggestion_list_padding_top);
+ }
this.setPaddingRelative(0, mBaseTopPadding, 0, mBaseBottomPadding);
// Disable the scrollbar since it causes the hover events happening near the
@@ -513,6 +517,8 @@ public class OmniboxSuggestionsDropdown extends RecyclerView {
mToolbarOnTop =
!(ChromeFeatureList.sAndroidBottomToolbarV2ReverseOrderSuggestionsList.getValue()
&& toolbarPosition == ControlsPosition.BOTTOM);
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled())
+ mToolbarOnTop = false;
mLayoutScrollListener.setToolbarPosition(mToolbarOnTop);
var params = (FrameLayout.LayoutParams) getLayoutParams();
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownEmbedder.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownEmbedder.java
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownEmbedder.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownEmbedder.java
@@ -5,11 +5,14 @@
package org.chromium.chrome.browser.omnibox.suggestions;
import android.annotation.SuppressLint;
+import android.view.View;
import org.chromium.base.Callback;
import org.chromium.build.annotations.NullMarked;
import org.chromium.build.annotations.Nullable;
+import androidx.annotation.NonNull;
+
import java.util.Objects;
/** Provider of capabilities required to embed the omnibox suggestion list into the UI. */
@@ -124,6 +127,9 @@ public interface OmniboxSuggestionsDropdownEmbedder {
*/
OmniboxAlignment getCurrentAlignment();
+ @NonNull
+ View getAnchorView();
+
/** Return whether the suggestions are being rendered in the tablet UI. */
boolean isTablet();
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
@@ -30,6 +30,7 @@ import androidx.core.widget.ImageViewCompat;
import org.chromium.build.annotations.NullMarked;
import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.omnibox.R;
import org.chromium.chrome.browser.omnibox.styles.OmniboxDrawableState;
import org.chromium.chrome.browser.omnibox.styles.OmniboxResourceProvider;
@@ -106,6 +107,12 @@ public final class BaseSuggestionViewBinder<T extends View>
updateColorScheme(model, view);
} else if (SuggestionCommonProperties.BG_BOTTOM_CORNER_ROUNDED == propertyKey
|| SuggestionCommonProperties.BG_TOP_CORNER_ROUNDED == propertyKey) {
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ view.setRoundingEdges(
+ model.get(SuggestionCommonProperties.BG_BOTTOM_CORNER_ROUNDED),
+ model.get(SuggestionCommonProperties.BG_TOP_CORNER_ROUNDED));
+ return;
+ }
view.setRoundingEdges(
model.get(SuggestionCommonProperties.BG_TOP_CORNER_ROUNDED),
model.get(SuggestionCommonProperties.BG_BOTTOM_CORNER_ROUNDED));
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -1667,6 +1667,12 @@ Your Google account may have other forms of browsing history like searches and a
<message name="IDS_FORCE_TABLET_UI_TITLE" desc="Title of the preference that allows the user to update force tablet UI settings.">
Force Tablet Mode
</message>
+ <message name="IDS_MOVE_TOOLBAR_BOTTOM_TITLE" desc="Title of the preference that allows the user to move toolbar on bottom.">
+ Move toolbar to bottom
+ </message>
+ <message name="IDS_DISABLE_TOOLBAR_SWIPE_UP" desc="Summary of the preference that allows the user to disable toolbar swipeup.">
+ Disable toolbar swipe up
+ </message>
<!-- Safety check -->
<message name="IDS_PREFS_SAFETY_CHECK" desc="Title of the Safety check element in settings, allowing the user to check multiple areas of browser safety. [CHAR_LIMIT=32]">
diff --git a/chrome/browser/ui/android/toolbar/BUILD.gn b/chrome/browser/ui/android/toolbar/BUILD.gn
--- a/chrome/browser/ui/android/toolbar/BUILD.gn
+++ b/chrome/browser/ui/android/toolbar/BUILD.gn
@@ -225,6 +225,7 @@ android_library("core_java") {
"//google_apis/gaia/android:java",
"//third_party/android_deps:material_design_java",
"//third_party/androidx:androidx_annotation_annotation_experimental_java",
+ "//third_party/androidx:androidx_coordinatorlayout_coordinatorlayout_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_appcompat_appcompat_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarFocusScrimHandler.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarFocusScrimHandler.java
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarFocusScrimHandler.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarFocusScrimHandler.java
@@ -24,6 +24,7 @@ import org.chromium.components.omnibox.OmniboxFeatures;
import org.chromium.ui.base.DeviceFormFactor;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.util.ColorUtils;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
/** Handles showing and hiding a scrim when url bar focus changes. */
@NullMarked
@@ -70,6 +71,10 @@ public class LocationBarFocusScrimHandler implements UrlFocusChangeListener {
mContext = context;
int topMargin = tabStripHeightSupplier.get();
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ // since the top bar is at the bottom, we need to cover the whole page
+ topMargin = 0;
+ }
mLightScrimColor = context.getColor(R.color.omnibox_focused_fading_background_color_light);
mScrimModel =
new PropertyModel.Builder(ScrimProperties.ALL_KEYS)
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarLongPressMenuHandler.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarLongPressMenuHandler.java
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarLongPressMenuHandler.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarLongPressMenuHandler.java
@@ -221,14 +221,6 @@ public class ToolbarLongPressMenuHandler implements ConfigurationChangedObserver
@VisibleForTesting
ModelList buildMenuItems(boolean onTop) {
ModelList itemList = new ModelList();
- itemList.add(
- new ListItemBuilder()
- .withTitleRes(
- onTop
- ? R.string.toolbar_move_to_the_bottom
- : R.string.toolbar_move_to_the_top)
- .withMenuId(MenuItemType.MOVE_ADDRESS_BAR_TO)
- .build());
itemList.add(
new ListItemBuilder()
.withTitleRes(R.string.toolbar_copy_link)
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsContentDelegate.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsContentDelegate.java
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsContentDelegate.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsContentDelegate.java
@@ -7,6 +7,12 @@ package org.chromium.chrome.browser.toolbar.bottom;
import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;
+import android.app.Activity;
+
+import org.chromium.base.supplier.NullableObservableSupplier;
+import org.chromium.chrome.browser.theme.TopUiThemeColorProvider;
+import org.chromium.chrome.browser.tab.Tab;
+
import org.chromium.base.Callback;
import org.chromium.build.annotations.NullMarked;
import org.chromium.chrome.browser.browser_controls.BottomControlsStacker.LayerScrollBehavior;
@@ -24,10 +30,13 @@ public interface BottomControlsContentDelegate extends BackPressHandler {
*
* @param visibilityController Bottom controls visibility controller.
* @param onModelTokenChange Callback to notify when a new capture is needed.
+ * @param topUiThemeColorProvider {@link ThemeColorProvider} for top UI.
+ * @param tabSupplier Activity tab supplier.
*/
void initializeWithNative(
BottomControlsCoordinator.BottomControlsVisibilityController visibilityController,
- Callback<Object> onModelTokenChange);
+ Callback<Object> onModelTokenChange,
+ TopUiThemeColorProvider topUiThemeColorProvider, NullableObservableSupplier<Tab> tabSupplier);
/** Destroy the delegate. */
void destroy();
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java
@@ -36,6 +36,8 @@ import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
import org.chromium.ui.resources.ResourceManager;
import org.chromium.ui.resources.dynamics.ViewResourceAdapter;
import org.chromium.ui.widget.Toast;
+import org.chromium.chrome.browser.theme.TopUiThemeColorProvider;
+import org.chromium.chrome.browser.tab.Tab;
import java.util.HashSet;
import java.util.Set;
@@ -94,6 +96,8 @@ public class BottomControlsCoordinator implements BackPressHandler {
* @param constraintsSupplier Used to access current constraints of the browser controls.
* @param readAloudRestoringSupplier Supplier that returns true if Read Aloud is currently
* restoring its player, e.g. after theme change.
+ * @param topUiThemeColorProvider {@link ThemeColorProvider} for top UI.
+ * @param tabSupplier Activity tab supplier.
*/
@SuppressLint("CutPasteId") // Not actually cut and paste since it's View vs ViewGroup.
public BottomControlsCoordinator(
@@ -110,7 +114,9 @@ public class BottomControlsCoordinator implements BackPressHandler {
TabObscuringHandler tabObscuringHandler,
NonNullObservableSupplier<Boolean> overlayPanelVisibilitySupplier,
NullableObservableSupplier<@BrowserControlsState Integer> constraintsSupplier,
- Supplier<Boolean> readAloudRestoringSupplier) {
+ Supplier<Boolean> readAloudRestoringSupplier,
+ TopUiThemeColorProvider topUiThemeColorProvider,
+ NullableObservableSupplier<Tab> tabSupplier) {
mRootFrameLayout = root;
root.setConstraintsSupplier(constraintsSupplier);
PropertyModel model = new PropertyModel(BottomControlsProperties.ALL_KEYS);
@@ -176,7 +182,8 @@ public class BottomControlsCoordinator implements BackPressHandler {
mMediator.setBottomControlsVisible(isVisible);
}
},
- root::onModelTokenChange);
+ root::onModelTokenChange,
+ topUiThemeColorProvider, tabSupplier);
mContentDelegateWrapper.set(contentDelegate);
},
mContentDelegateSupplier,
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsMediator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsMediator.java
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsMediator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsMediator.java
@@ -30,6 +30,7 @@ import org.chromium.ui.KeyboardVisibilityDelegate;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.edge_to_edge.EdgeToEdgeSupplier.ChangeObserver;
import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
import java.util.function.Supplier;
@@ -170,6 +171,12 @@ class BottomControlsMediator
void setBottomControlsVisible(boolean visible) {
boolean visibilityChanged = mIsBottomControlsVisible != visible;
+ if (visible == true
+ && mIsBottomControlsVisible == false
+ && ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ // always show the toolbar if the bottom controls are visible, so as not to leave the hole below.
+ visibilityChanged = true;
+ }
mIsBottomControlsVisible = visible;
updateCompositedViewVisibility();
updateAndroidViewVisibility();
@@ -199,6 +206,20 @@ class BottomControlsMediator
mTabObscuringHandler.removeObserver(this);
}
+ @Override
+ public void onControlsOffsetChanged(
+ int topOffset,
+ int topControlsMinHeightOffset,
+ boolean topControlsMinHeightChanged,
+ int bottomOffset,
+ int bottomControlsMinHeightOffset,
+ boolean bottomControlsMinHeightChanged,
+ boolean requestNewFrame,
+ boolean isVisibilityForced) {
+ mModel.set(BottomControlsProperties.TOPCONTROLSMINHEIGHT_OFFSET, topControlsMinHeightOffset);
+ mModel.set(BottomControlsProperties.Y_OFFSET, -topOffset);
+ }
+
@Override
public void onBottomControlsHeightChanged(
int bottomControlsHeight, int bottomControlsMinHeight) {
@@ -288,11 +309,13 @@ class BottomControlsMediator
&& !mIsInSwipeLayout
&& getBrowserControls().getBottomControlOffset() == 0;
if (visible) {
- // Translate view so that its bottom is aligned with the "base" y_offset, or the
- // y_offset when the bottom controls aren't offset.
- mModel.set(
- BottomControlsProperties.ANDROID_VIEW_TRANSLATE_Y,
- mModel.get(BottomControlsProperties.Y_OFFSET));
+ if (!ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ // Translate view so that its bottom is aligned with the "base" y_offset, or the
+ // y_offset when the bottom controls aren't offset.
+ mModel.set(
+ BottomControlsProperties.ANDROID_VIEW_TRANSLATE_Y,
+ mModel.get(BottomControlsProperties.Y_OFFSET));
+ }
}
mModel.set(BottomControlsProperties.ANDROID_VIEW_VISIBLE, visible);
}
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsProperties.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsProperties.java
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsProperties.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsProperties.java
@@ -25,6 +25,9 @@ class BottomControlsProperties {
*/
static final WritableIntPropertyKey ANDROID_VIEW_TRANSLATE_Y = new WritableIntPropertyKey();
+ /** The min height of browser controls in px. */
+ static final WritableIntPropertyKey TOPCONTROLSMINHEIGHT_OFFSET = new WritableIntPropertyKey();
+
/** Whether the Android view version of the bottom controls component is visible. */
static final WritableBooleanPropertyKey ANDROID_VIEW_VISIBLE = new WritableBooleanPropertyKey();
@@ -48,6 +51,7 @@ class BottomControlsProperties {
ANDROID_VIEW_VISIBLE,
COMPOSITED_VIEW_VISIBLE,
IS_OBSCURED,
- OFFSET_TAG
+ OFFSET_TAG,
+ TOPCONTROLSMINHEIGHT_OFFSET
};
}
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java
@@ -56,6 +56,8 @@ class BottomControlsViewBinder {
view.sceneLayer.setYOffset(model.get(BottomControlsProperties.Y_OFFSET));
} else if (BottomControlsProperties.ANDROID_VIEW_TRANSLATE_Y == propertyKey) {
view.root.setTranslationY(model.get(BottomControlsProperties.ANDROID_VIEW_TRANSLATE_Y));
+ } else if (BottomControlsProperties.TOPCONTROLSMINHEIGHT_OFFSET == propertyKey) {
+ view.sceneLayer.setTopControlsMinHeightOffset(model.get(BottomControlsProperties.TOPCONTROLSMINHEIGHT_OFFSET));
} else if (BottomControlsProperties.ANDROID_VIEW_VISIBLE == propertyKey
|| BottomControlsProperties.COMPOSITED_VIEW_VISIBLE == propertyKey) {
final boolean showAndroidView =
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewSceneLayer.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewSceneLayer.java
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewSceneLayer.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewSceneLayer.java
@@ -18,6 +18,8 @@ import org.chromium.chrome.browser.layouts.scene_layer.SceneOverlayLayer;
import org.chromium.components.browser_ui.widget.ViewResourceFrameLayout;
import org.chromium.ui.resources.ResourceManager;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+
/**
* A composited view that sits at the bottom of the screen and listens to changes in the browser
* controls. When visible, the view will mimic the behavior of the top browser controls when
@@ -38,6 +40,9 @@ public class ScrollingBottomViewSceneLayer extends SceneOverlayLayer implements
/** The current Y offset of the bottom view in px. */
private int mCurrentYOffsetPx;
+ /** The min height of browser controls in px. */
+ private int mTopControlsMinHeightOffset;
+
/** The current X offset of the bottom view in px. */
private int mCurrentXOffsetPx;
@@ -95,6 +100,13 @@ public class ScrollingBottomViewSceneLayer extends SceneOverlayLayer implements
mOffsetTag = offsetTag;
}
+ /**
+ * @param offsetPx The min height of browser controls in px.
+ */
+ public void setTopControlsMinHeightOffset(int offsetPx) {
+ mTopControlsMinHeightOffset = offsetPx;
+ }
+
/**
* @param visible Whether this {@link SceneLayer} is visible.
*/
@@ -118,6 +130,11 @@ public class ScrollingBottomViewSceneLayer extends SceneOverlayLayer implements
@Override
public SceneOverlayLayer getUpdatedSceneOverlayTree(
RectF viewport, RectF visibleViewport, ResourceManager resourceManager) {
+ float offsetPy = viewport.height() + mCurrentYOffsetPx;
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ // fix the offset of the fake bottom controls, used only for animations
+ offsetPy -= (mBottomView.getHeight() - mCurrentYOffsetPx + mTopControlsMinHeightOffset);
+ }
ScrollingBottomViewSceneLayerJni.get()
.updateScrollingBottomViewLayer(
mNativePtr,
@@ -125,7 +142,7 @@ public class ScrollingBottomViewSceneLayer extends SceneOverlayLayer implements
mResourceId,
mTopShadowHeightPx,
mCurrentXOffsetPx,
- viewport.height() + mCurrentYOffsetPx,
+ offsetPy,
true,
mOffsetTag);
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/settings/AddressBarPreference.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/settings/AddressBarPreference.java
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/settings/AddressBarPreference.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/settings/AddressBarPreference.java
@@ -99,6 +99,7 @@ public class AddressBarPreference extends ContainedRadioButtonGroupPreference
* configurable for experimental purposes but defaults to top.
*/
public static boolean isToolbarConfiguredToShowOnTop() {
+ if ((true)) return true;
// enumToBoolean is true if position is bottom, but this method wants whether it's on top.
var t = computeToolbarPositionAndSource();
return !isToolbarAtBottom(t);
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java
@@ -82,6 +82,10 @@ import java.lang.annotation.RetentionPolicy;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
+import android.view.Gravity;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
+
/** Layout for the browser controls (omnibox, menu, tab strip, etc..). */
@NullMarked
public class ToolbarControlContainer extends OptimizedFrameLayout
@@ -182,6 +186,11 @@ public class ToolbarControlContainer extends OptimizedFrameLayout
Resources res = getContext().getResources();
setMinimumHeight((int) res.getDimension(toolbarLayoutHeightResId));
}
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ // the top toolbar is docked at the bottom
+ CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams)getLayoutParams();
+ layoutParams.gravity = Gravity.START | Gravity.BOTTOM;
+ }
mToolbarContainer = findViewById(R.id.toolbar_container);
mToolbarLayoutHeight = getResources().getDimensionPixelSize(toolbarLayoutHeightResId);
ViewStub toolbarStub = findViewById(R.id.toolbar_stub);
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
@@ -1074,6 +1074,6 @@ public class TopToolbarCoordinator implements Toolbar, TopControlLayer {
hairlineAdjustment = -mControlContainer.getToolbarHairlineHeight();
}
- assertNonNull(mOverlayCoordinator).setYOffset(mLayerYOffset - diff + hairlineAdjustment);
+ assertNonNull(mOverlayCoordinator).setYOffsetLegacy(mLayerYOffset - diff + hairlineAdjustment);
}
}
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayCoordinator.java
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayCoordinator.java
@@ -24,11 +24,14 @@ import org.chromium.chrome.browser.theme.TopUiThemeColorProvider;
import org.chromium.chrome.browser.toolbar.R;
import org.chromium.chrome.browser.toolbar.ToolbarProgressBar;
import org.chromium.components.browser_ui.widget.ClipDrawableProgressBar;
+import org.chromium.ui.base.DeviceFormFactor;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.resources.ResourceManager;
import java.util.function.Supplier;
+import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
+
/** The public interface for the top toolbar texture component. */
@NullMarked
public class TopToolbarOverlayCoordinator implements SceneOverlay {
@@ -76,6 +79,12 @@ public class TopToolbarOverlayCoordinator implements SceneOverlay {
browserControlsStateProvider.getContentOffset())
.with(TopToolbarOverlayProperties.ANONYMIZE, false)
.with(TopToolbarOverlayProperties.SHOW_SHADOW, true)
+ .with(TopToolbarOverlayProperties.VIEWPORT_HEIGHT, 0)
+ .with(TopToolbarOverlayProperties.TOOLBAR_HEIGHT,
+ browserControlsStateProvider.getTopControlsHeight()
+ + (DeviceFormFactor.isNonMultiDisplayContextOnTablet(context)
+ ? context.getResources().getDimensionPixelSize(R.dimen.toolbar_hairline_height)
+ : -context.getResources().getDimensionPixelSize(R.dimen.toolbar_hairline_height)))
.build();
mSceneLayer = new TopToolbarSceneLayer(resourceManagerSupplier);
mChangeProcessor =
@@ -117,7 +126,7 @@ public class TopToolbarOverlayCoordinator implements SceneOverlay {
}
/** Set the yOffset */
- public void setYOffset(float yOffset) {
+ public void setYOffsetLegacy(float yOffset) {
mMediator.setYOffset(yOffset);
}
@@ -151,6 +160,7 @@ public class TopToolbarOverlayCoordinator implements SceneOverlay {
@Override
public SceneOverlayLayer getUpdatedSceneOverlayTree(
RectF viewport, RectF visibleViewport, ResourceManager resourceManager) {
+ mModel.set(TopToolbarOverlayProperties.VIEWPORT_HEIGHT, viewport.height());
return mSceneLayer;
}
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayProperties.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayProperties.java
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayProperties.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayProperties.java
@@ -71,6 +71,12 @@ public class TopToolbarOverlayProperties {
public static final WritableLongPropertyKey CAPTURE_RESOURCE_ID = new WritableLongPropertyKey();
+ /** The current height of the main visible view. */
+ public static final WritableFloatPropertyKey VIEWPORT_HEIGHT = new WritableFloatPropertyKey();
+
+ /** The current height of the top toolbar. */
+ public static final WritableFloatPropertyKey TOOLBAR_HEIGHT = new WritableFloatPropertyKey();
+
public static final PropertyKey[] ALL_KEYS =
new PropertyKey[] {
ANONYMIZE,
@@ -86,5 +92,5 @@ public class TopToolbarOverlayProperties {
LEGACY_CONTENT_OFFSET,
TOOLBAR_OFFSET_TAG,
CAPTURE_RESOURCE_ID
- };
+ , VIEWPORT_HEIGHT, TOOLBAR_HEIGHT};
}
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarSceneLayer.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarSceneLayer.java
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarSceneLayer.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarSceneLayer.java
@@ -16,6 +16,7 @@ import org.chromium.components.browser_ui.widget.ClipDrawableProgressBar.Drawing
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.resources.ResourceManager;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
import java.util.function.Supplier;
@@ -45,6 +46,15 @@ public class TopToolbarSceneLayer extends SceneOverlayLayer {
/** Push all information about the texture to native at once. */
private void pushProperties(PropertyModel model) {
if (mResourceManagerSupplier.get() == null) return;
+ float contentOffset = model.get(TopToolbarOverlayProperties.LEGACY_CONTENT_OFFSET);
+ if (contentOffset == -10001 /*INVALID_CONTENT_OFFSET*/) return;
+ float yOffset = model.get(TopToolbarOverlayProperties.Y_OFFSET);
+ if (ChromeFeatureList.sMoveTopToolbarToBottom.isEnabled()) {
+ // fix the offset of the fake top controls, used only for animations
+ yOffset = model.get(TopToolbarOverlayProperties.VIEWPORT_HEIGHT) -
+ model.get(TopToolbarOverlayProperties.TOOLBAR_HEIGHT);
+ }
+
TopToolbarSceneLayerJni.get()
.updateToolbarLayer(
mNativePtr,
@@ -54,8 +64,8 @@ public class TopToolbarSceneLayer extends SceneOverlayLayer {
model.get(TopToolbarOverlayProperties.URL_BAR_RESOURCE_ID),
model.get(TopToolbarOverlayProperties.URL_BAR_COLOR),
model.get(TopToolbarOverlayProperties.X_OFFSET),
- model.get(TopToolbarOverlayProperties.Y_OFFSET),
- model.get(TopToolbarOverlayProperties.LEGACY_CONTENT_OFFSET),
+ yOffset,
+ contentOffset,
model.get(TopToolbarOverlayProperties.SHOW_SHADOW),
model.get(TopToolbarOverlayProperties.VISIBLE),
model.get(TopToolbarOverlayProperties.ANONYMIZE),
diff --git a/components/browser_ui/accessibility/android/java/res/xml/accessibility_preferences.xml b/components/browser_ui/accessibility/android/java/res/xml/accessibility_preferences.xml
--- a/components/browser_ui/accessibility/android/java/res/xml/accessibility_preferences.xml
+++ b/components/browser_ui/accessibility/android/java/res/xml/accessibility_preferences.xml
@@ -76,4 +76,12 @@ found in the LICENSE file.
app:featureName="disable-pull-to-refresh-effect"
app:needRestart="true" />
+ <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
+ android:key="move_toolbar_bottom"
+ android:title="@string/move_toolbar_bottom_title" />
+
+ <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
+ android:key="disable_toolbar_swipeup"
+ android:title="@string/disable_toolbar_swipe_up" />
+
</PreferenceScreen>
diff --git a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettings.java b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettings.java
--- a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettings.java
+++ b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettings.java
@@ -84,6 +84,11 @@ public class AccessibilitySettings extends ChromeBaseSettingsFragment
private BooleanPreferenceDelegate mForceTabletUIDelegate;
static final String PREF_FORCE_TABLET_UI = "force_tablet_ui";
+ static final String PREF_MOVE_TOOLBAR_TO_BOTTOM = "move_toolbar_bottom";
+ static final String PREF_DISABLE_TOOLBAR_SWIPE_UP = "disable_toolbar_swipeup";
+ private BooleanPreferenceDelegate mMoveTopToolbarToBottomDelegate;
+ private BooleanPreferenceDelegate mDisableToolbarSwipeUpDelegate;
+
private PageZoomPreference mPageZoomDefaultZoomPref;
private ChromeSwitchPreference mPageZoomIncludeOSAdjustment;
private ChromeSwitchPreference mPageZoomAlwaysShowPref;
@@ -102,6 +107,10 @@ public class AccessibilitySettings extends ChromeBaseSettingsFragment
mDelegate = delegate;
}
+ public AccessibilitySettingsDelegate getDelegate() {
+ return mDelegate;
+ }
+
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
@@ -177,6 +186,18 @@ public class AccessibilitySettings extends ChromeBaseSettingsFragment
forceTabletUiPref.setChecked(mForceTabletUIDelegate.getValue());
forceTabletUiPref.setOnPreferenceChangeListener(this);
+ ChromeSwitchPreference mMoveToolbarToBottomPref =
+ (ChromeSwitchPreference) findPreference(PREF_MOVE_TOOLBAR_TO_BOTTOM);
+ mMoveTopToolbarToBottomDelegate = mDelegate.getMoveTopToolbarToBottomDelegate();
+ mMoveToolbarToBottomPref.setChecked(mMoveTopToolbarToBottomDelegate.getValue());
+ mMoveToolbarToBottomPref.setOnPreferenceChangeListener(this);
+
+ ChromeSwitchPreference mDisableToolbarSwipeUpPref =
+ (ChromeSwitchPreference) findPreference(PREF_DISABLE_TOOLBAR_SWIPE_UP);
+ mDisableToolbarSwipeUpDelegate = mDelegate.getDisableToolbarSwipeUpDelegate();
+ mDisableToolbarSwipeUpPref.setChecked(mDisableToolbarSwipeUpDelegate.getValue());
+ mDisableToolbarSwipeUpPref.setOnPreferenceChangeListener(this);
+
Preference captions = findPreference(PREF_CAPTIONS);
captions.setOnPreferenceClickListener(
preference -> {
@@ -296,6 +317,12 @@ public class AccessibilitySettings extends ChromeBaseSettingsFragment
mDelegate.getBrowserContextHandle(), (Integer) newValue);
} else if (PREF_PAGE_ZOOM_ALWAYS_SHOW.equals(preference.getKey())) {
PageZoomUtils.setShouldAlwaysShowZoomMenuItem((Boolean) newValue);
+ } else if (PREF_MOVE_TOOLBAR_TO_BOTTOM.equals(preference.getKey())) {
+ mMoveTopToolbarToBottomDelegate.setValue((Boolean) newValue);
+ mDelegate.requestRestart(getActivity());
+ } else if (PREF_DISABLE_TOOLBAR_SWIPE_UP.equals(preference.getKey())) {
+ mDisableToolbarSwipeUpDelegate.setValue((Boolean) newValue);
+ mDelegate.requestRestart(getActivity());
} else if (PREF_PAGE_ZOOM_INCLUDE_OS_ADJUSTMENT.equals(preference.getKey())) {
// TODO(mschillaci): Implement the override behavior for OS level.
} else if (OmniboxFeatures.KEY_JUMP_START_OMNIBOX.equals(preference.getKey())) {
diff --git a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettingsDelegate.java b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettingsDelegate.java
--- a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettingsDelegate.java
+++ b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettingsDelegate.java
@@ -5,6 +5,9 @@
package org.chromium.components.browser_ui.accessibility;
import org.chromium.build.annotations.NullMarked;
+
+import android.app.Activity;
+
import org.chromium.components.browser_ui.settings.SettingsNavigation;
import org.chromium.components.dom_distiller.core.DistilledPagePrefs;
import org.chromium.content_public.browser.BrowserContextHandle;
@@ -37,6 +40,11 @@ public interface AccessibilitySettingsDelegate {
void setValue(boolean value);
}
+ void requestRestart(Activity activity);
+
+ BooleanPreferenceDelegate getMoveTopToolbarToBottomDelegate();
+ BooleanPreferenceDelegate getDisableToolbarSwipeUpDelegate();
+
/**
* @return The BrowserContextHandle that should be used to read and update settings.
*/
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -34,6 +34,7 @@
namespace features {
#if BUILDFLAG(IS_ANDROID)
+
// If this flag is enabled, a DumpWithoutCrashing() is captured when a bad
// state is detected when moving the composited UI. For example, this could
// mean scrolling without a resource, or OffsetTagValues trying to position
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -35,6 +35,7 @@
#include "base/task/thread_pool.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/trace_event/trace_event.h"
+#include "cc/base/features.h"
#include "cc/base/math_util.h"
#include "cc/slim/layer.h"
#include "components/input/cursor_manager.h"
@@ -954,6 +955,8 @@ void RenderWidgetHostViewAndroid::OnRenderFrameMetadataChangedBeforeActivation(
// factor. Thus, |top_content_offset| in CSS pixels is also in DIPs.
float top_content_offset =
metadata.top_controls_height * metadata.top_controls_shown_ratio;
+ if (base::FeatureList::IsEnabled(::features::kMoveTopToolbarToBottom))
+ top_content_offset = 0;
float top_shown_pix = top_content_offset;
if (ime_adapter_android_) {
diff --git a/cromite_flags/chrome/browser/about_flags_cc/Move-navigation-bar-to-bottom.inc b/cromite_flags/chrome/browser/about_flags_cc/Move-navigation-bar-to-bottom.inc
new file mode 100644
--- /dev/null
+++ b/cromite_flags/chrome/browser/about_flags_cc/Move-navigation-bar-to-bottom.inc
@@ -0,0 +1,17 @@
+#if BUILDFLAG(IS_ANDROID)
+
+#ifdef FLAG_SECTION
+
+ {"move-top-toolbar-to-bottom",
+ "Move top toolbar to bottom",
+ "Move the top toolbar to the bottom.", kOsAndroid,
+ FEATURE_VALUE_TYPE(features::kMoveTopToolbarToBottom)},
+
+ {"disable-toolbar-swipe-up",
+ "Disable toolbar swipe up",
+ "Disable toolbar swipe up.", kOsAndroid,
+ FEATURE_VALUE_TYPE(features::kDisableToolbarSwipeUp)},
+
+#endif
+
+#endif
diff --git a/third_party/blink/renderer/core/frame/viewport_data.cc b/third_party/blink/renderer/core/frame/viewport_data.cc
--- a/third_party/blink/renderer/core/frame/viewport_data.cc
+++ b/third_party/blink/renderer/core/frame/viewport_data.cc
@@ -175,6 +175,7 @@ void ViewportData::SetExpandIntoDisplayCutout(bool expand) {
}
void ViewportData::SetVirtualKeyboardOverlaysContent(bool overlays_content) {
+ overlays_content = false;
if (virtual_keyboard_overlays_content_ == overlays_content)
return;
--