Commit b59463f1 authored by Evan Stade's avatar Evan Stade Committed by Commit Bot

WebLayer: add support for display cutout

DisplayCutoutController is moved to //components.

Fixed: 1095714

Change-Id: I3994afeecaa9d555514b571727da8d0efae2a55f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2309036Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarBecca Hughes <beccahughes@chromium.org>
Commit-Queue: Evan Stade <estade@chromium.org>
Cr-Commit-Position: refs/heads/master@{#792733}
parent ea267e0b
......@@ -351,6 +351,7 @@ android_library("chrome_java") {
"//components/browser_ui/android/bottomsheet:manager_java",
"//components/browser_ui/banners/android:java",
"//components/browser_ui/client_certificate/android:java",
"//components/browser_ui/display_cutout/android:java",
"//components/browser_ui/http_auth/android:java",
"//components/browser_ui/media/android:java",
"//components/browser_ui/modaldialog/android:java",
......@@ -805,6 +806,7 @@ junit_binary("chrome_junit_tests") {
"//components/background_task_scheduler:background_task_scheduler_task_ids_java",
"//components/bookmarks/common/android:bookmarks_java",
"//components/browser_ui/android/bottomsheet:java",
"//components/browser_ui/display_cutout/android:java",
"//components/browser_ui/media/android:java",
"//components/browser_ui/notifications/android:java",
"//components/browser_ui/share/android:java",
......@@ -1010,6 +1012,7 @@ android_library("chrome_test_java") {
"//components/bookmarks/common/android:bookmarks_java",
"//components/browser_ui/android/bottomsheet:java",
"//components/browser_ui/android/bottomsheet/test:java",
"//components/browser_ui/display_cutout/android:java",
"//components/browser_ui/modaldialog/android:java",
"//components/browser_ui/modaldialog/android:javatests",
"//components/browser_ui/notifications/android:java",
......
......@@ -14,6 +14,7 @@ include_rules = [
"+chrome/browser/xsurface/android",
"+components/browser_ui/android/bottomsheet",
"+components/browser_ui/banners/android",
"+components/browser_ui/display_cutout/android",
"+components/browser_ui/media/android",
"+components/browser_ui/modaldialog/android",
"+components/browser_ui/share/android",
......
......@@ -511,7 +511,6 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/directactions/GoBackDirectActionHandler.java",
"java/src/org/chromium/chrome/browser/directactions/MenuDirectActionHandler.java",
"java/src/org/chromium/chrome/browser/directactions/SimpleDirectActionHandler.java",
"java/src/org/chromium/chrome/browser/display_cutout/DisplayCutoutController.java",
"java/src/org/chromium/chrome/browser/display_cutout/DisplayCutoutTabHelper.java",
"java/src/org/chromium/chrome/browser/document/ChromeIntentUtil.java",
"java/src/org/chromium/chrome/browser/document/ChromeLauncherActivity.java",
......
......@@ -16,6 +16,7 @@ import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabObserver;
import org.chromium.chrome.browser.tab.TabSelectionType;
import org.chromium.components.browser_ui.display_cutout.DisplayCutoutController;
import org.chromium.components.browser_ui.widget.InsetObserverView;
import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.browser.WebContentsObserver;
......
......@@ -22,6 +22,7 @@ import org.chromium.chrome.browser.fullscreen.FullscreenManager;
import org.chromium.chrome.browser.fullscreen.FullscreenOptions;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.components.browser_ui.display_cutout.DisplayCutoutController;
import org.chromium.content_public.browser.WebContentsObserver;
import org.chromium.content_public.browser.test.util.Criteria;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
......
......@@ -30,6 +30,7 @@ import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.tab.TabImpl;
import org.chromium.chrome.browser.tab.TabObserver;
import org.chromium.chrome.browser.tab.TabSelectionType;
import org.chromium.components.browser_ui.display_cutout.DisplayCutoutController;
import org.chromium.components.browser_ui.widget.InsetObserverView;
import org.chromium.content_public.browser.WebContents;
import org.chromium.testing.local.LocalRobolectricTestRunner;
......
beccahughes@chromium.org
mlamouri@chromium.org
# COMPONENT: Blink>Layout
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/config/android/rules.gni")
android_library("java") {
sources = [ "java/src/org/chromium/components/browser_ui/display_cutout/DisplayCutoutController.java" ]
deps = [
"//base:base_java",
"//components/browser_ui/widget/android:java",
"//content/public/android:content_java",
"//third_party/android_deps:androidx_annotation_annotation_java",
"//third_party/blink/public/mojom:mojom_platform_java",
"//ui/android:ui_java",
]
}
include_rules = [
"+content/public/android",
"+ui/android",
]
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.display_cutout;
package org.chromium.components.browser_ui.display_cutout;
import android.annotation.TargetApi;
import android.app.Activity;
......@@ -42,7 +42,7 @@ public class DisplayCutoutController implements InsetObserverView.WindowInsetObs
private @Nullable InsetObserverView mInsetObserverView;
/** An interface for providing embedder-specific behavior to the controller. */
interface Delegate {
public interface Delegate {
/** Returns the activity this controller is associated with, if there is one. */
@Nullable
Activity getAttachedActivity();
......@@ -147,7 +147,7 @@ public class DisplayCutoutController implements InsetObserverView.WindowInsetObs
*/
@VisibleForTesting
@TargetApi(Build.VERSION_CODES.P)
protected int getDisplayCutoutMode() {
public int getDisplayCutoutMode() {
// If we are not interactable then force the default mode.
if (!mDelegate.isInteractable()) {
return LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
......@@ -175,18 +175,22 @@ public class DisplayCutoutController implements InsetObserverView.WindowInsetObs
mWindow.setAttributes(attributes);
}
/** Updates the layout based on internal state. */
void maybeUpdateLayout() {
/** Should be called to refresh the activity window's layout based on current state. */
public void maybeUpdateLayout() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return;
LayoutParams attributes = getWindowAttributes();
if (attributes == null) return;
attributes.layoutInDisplayCutoutMode = getDisplayCutoutMode();
final int displayCutoutMode = getDisplayCutoutMode();
if (attributes.layoutInDisplayCutoutMode == displayCutoutMode) return;
attributes.layoutInDisplayCutoutMode = displayCutoutMode;
setWindowAttributes(attributes);
}
void onActivityAttachmentChanged(@Nullable WindowAndroid window) {
/** Should be called when the associated UI surface is attached or detached to an activity. */
public void onActivityAttachmentChanged(@Nullable WindowAndroid window) {
if (window == null) {
maybeRemoveInsetObserver();
} else {
......
......@@ -13,6 +13,7 @@ android_library("weblayer_java_tests") {
"src/org/chromium/weblayer/test/CookieManagerTest.java",
"src/org/chromium/weblayer/test/CrashReporterTest.java",
"src/org/chromium/weblayer/test/DataClearingTest.java",
"src/org/chromium/weblayer/test/DisplayCutoutTest.java",
"src/org/chromium/weblayer/test/DowngradeTest.java",
"src/org/chromium/weblayer/test/DownloadCallbackTest.java",
"src/org/chromium/weblayer/test/ErrorPageCallbackTest.java",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.weblayer.test;
import android.os.Build;
import android.view.WindowManager.LayoutParams;
import androidx.test.filters.SmallTest;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.test.util.MinAndroidSdkLevel;
import org.chromium.content_public.browser.test.util.Criteria;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.weblayer.shell.InstrumentationActivity;
/**
* Tests that viewport-fit is respected.
*/
@RunWith(WebLayerJUnit4ClassRunner.class)
public class DisplayCutoutTest {
@Rule
public InstrumentationActivityTestRule mActivityTestRule =
new InstrumentationActivityTestRule();
private InstrumentationActivity mActivity;
@Before
public void setUp() {
String url = mActivityTestRule.getTestDataURL("display_cutout.html");
mActivity = mActivityTestRule.launchShellWithUrl(url);
Assert.assertNotNull(mActivity);
}
@MinWebLayerVersion(86)
@Test
@SmallTest
@MinAndroidSdkLevel(Build.VERSION_CODES.P)
public void testWithFullscreen() {
Assert.assertEquals(mActivity.getWindow().getAttributes().layoutInDisplayCutoutMode,
LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT);
// First touch enters fullscreen.
EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView());
CriteriaHelper.pollInstrumentationThread(() -> {
Criteria.checkThat(
mActivityTestRule.executeScriptAndExtractBoolean("document.webkitIsFullScreen"),
Matchers.is(true));
});
mActivityTestRule.executeScriptSync("setViewportFit(\"contain\")", false);
CriteriaHelper.pollUiThread(() -> {
Criteria.checkThat(mActivity.getWindow().getAttributes().layoutInDisplayCutoutMode,
Matchers.is(LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER));
});
}
@MinWebLayerVersion(86)
@Test
@SmallTest
@MinAndroidSdkLevel(Build.VERSION_CODES.P)
public void testWithNoFullscreen() {
Assert.assertEquals(mActivity.getWindow().getAttributes().layoutInDisplayCutoutMode,
LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT);
Assert.assertFalse(
mActivityTestRule.executeScriptAndExtractBoolean("document.webkitIsFullScreen"));
mActivityTestRule.executeScriptSync("setViewportFit(\"contain\")", false);
try {
// When not in fullscreen, this criterion will not be fulfilled.
CriteriaHelper.pollUiThread(() -> {
Criteria.checkThat(mActivity.getWindow().getAttributes().layoutInDisplayCutoutMode,
Matchers.not(LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT));
});
} catch (AssertionError e) {
}
Assert.assertEquals(mActivity.getWindow().getAttributes().layoutInDisplayCutoutMode,
LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT);
}
}
......@@ -136,6 +136,7 @@ android_library("java") {
"//components/autofill/android/provider:java",
"//components/browser_ui/banners/android:java",
"//components/browser_ui/client_certificate/android:java",
"//components/browser_ui/display_cutout/android:java",
"//components/browser_ui/http_auth/android:java",
"//components/browser_ui/media/android:java",
"//components/browser_ui/modaldialog/android:java",
......
......@@ -17,6 +17,7 @@ import android.widget.RelativeLayout;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.components.browser_ui.modaldialog.AppModalPresenter;
import org.chromium.components.browser_ui.widget.InsetObserverView;
import org.chromium.components.embedder_support.view.ContentView;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.modaldialog.DialogDismissalCause;
......@@ -134,6 +135,10 @@ public final class BrowserViewController
return mContentViewRenderView;
}
public InsetObserverView getInsetObserverView() {
return mContentViewRenderView.getInsetObserverView();
}
/** Returns the ViewGroup into which the InfoBarContainer should be parented. **/
public ViewGroup getInfoBarContainerParentView() {
return mContentViewRenderView;
......
......@@ -741,6 +741,10 @@ public class ContentViewRenderView extends RelativeLayout {
}
}
public InsetObserverView getInsetObserverView() {
return mInsetObserverView;
}
/**
* Should be called when the ContentViewRenderView is not needed anymore so its associated
* native resource can be freed.
......
......@@ -4,6 +4,7 @@
package org.chromium.weblayer_private;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.RectF;
import android.os.Build;
......@@ -26,10 +27,12 @@ import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.components.autofill.AutofillActionModeCallback;
import org.chromium.components.autofill.AutofillProvider;
import org.chromium.components.browser_ui.display_cutout.DisplayCutoutController;
import org.chromium.components.browser_ui.http_auth.LoginPrompt;
import org.chromium.components.browser_ui.media.MediaSessionHelper;
import org.chromium.components.browser_ui.util.BrowserControlsVisibilityDelegate;
import org.chromium.components.browser_ui.util.ComposedBrowserControlsVisibilityDelegate;
import org.chromium.components.browser_ui.widget.InsetObserverView;
import org.chromium.components.embedder_support.contextmenu.ContextMenuParams;
import org.chromium.components.external_intents.InterceptNavigationDelegateImpl;
import org.chromium.components.find_in_page.FindInPageBridge;
......@@ -117,6 +120,7 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer {
private InterceptNavigationDelegateImpl mInterceptNavigationDelegate;
private InfoBarContainer mInfoBarContainer;
private MediaSessionHelper mMediaSessionHelper;
private DisplayCutoutController mDisplayCutoutController;
private boolean mPostContainerViewInitDone;
......@@ -242,6 +246,11 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer {
hideFindInPageUiAndNotifyClient();
}
}
@Override
public void viewportFitChanged(@WebContentsObserver.ViewportFitType int value) {
ensureDisplayCutoutController();
mDisplayCutoutController.setViewportFit(value);
}
};
mWebContents.addObserver(mWebContentsObserver);
......@@ -346,6 +355,7 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer {
public void updateViewAttachedStateFromBrowser() {
updateWebContentsVisibility();
updateDisplayCutoutController();
}
public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) {
......@@ -390,6 +400,7 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer {
mNativeTab, topControlsContainerViewHandle, bottomControlsContainerViewHandle);
mInfoBarContainer.onTabDidGainActive();
updateWebContentsVisibility();
updateDisplayCutoutController();
}
/**
......@@ -402,6 +413,7 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer {
hideFindInPageUiAndNotifyClient();
updateWebContentsVisibility();
updateDisplayCutoutController();
// This method is called as part of the final phase of TabImpl destruction, at which
// point mInfoBarContainer has already been destroyed.
......@@ -431,6 +443,13 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer {
}
}
private void updateDisplayCutoutController() {
if (mDisplayCutoutController == null) return;
mDisplayCutoutController.onActivityAttachmentChanged(mBrowser.getWindowAndroid());
mDisplayCutoutController.maybeUpdateLayout();
}
public void loadUrl(LoadUrlParams loadUrlParams) {
String url = loadUrlParams.getUrl();
if (url == null || url.isEmpty()) return;
......@@ -955,6 +974,34 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer {
TabImplJni.get().updateBrowserControlsConstraint(mNativeTab, constraint, animate);
}
private void ensureDisplayCutoutController() {
if (mDisplayCutoutController != null) return;
mDisplayCutoutController =
new DisplayCutoutController(new DisplayCutoutController.Delegate() {
@Override
public Activity getAttachedActivity() {
WindowAndroid window = mBrowser.getWindowAndroid();
return window == null ? null : window.getActivity().get();
}
@Override
public WebContents getWebContents() {
return mWebContents;
}
@Override
public InsetObserverView getInsetObserverView() {
return mBrowser.getViewController().getInsetObserverView();
}
@Override
public boolean isInteractable() {
return isVisible();
}
});
}
/**
* Returns the BrowserViewController for this TabImpl, but only if this
* is the active TabImpl.
......
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1"></meta>
</head>
<body>
<p>Greetings planet</p>
</body>
<script>
function toggleFullscreen() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen();
} else {
document.exitFullscreen();
}
}
document.addEventListener('click', function(e) { toggleFullscreen(); }, false);
const viewport = document.getElementsByTagName('meta')[0];
const defaultValue = viewport.getAttribute('content');
function setViewportFit(fit) {
if (fit) {
viewport.setAttribute('content', defaultValue + ', viewport-fit=' + fit);
} else {
viewport.setAttribute('content', defaultValue);
}
}
</script>
</html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment