Commit bf026b2f authored by Wenyu Fu's avatar Wenyu Fu Committed by Commit Bot

[CCTToSFRE] Change the FRE layout

Change the FRE UI to better adapt to the transition from loading to ToS.

Layout preview: go/clank-fre-ui-preview

This CL also changes the visibility of UMA / ToS component to View.GONE
when they are hidden. This will free up the space so that the screen
will not be scrollable on blank content.

Bug: 1108558, 1117352
Change-Id: I3f8616bd5810da71afa8b6e40dea2db92e89013e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2352033
Commit-Queue: Wenyu Fu <wenyufu@chromium.org>
Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Reviewed-by: default avatarSky Malice <skym@chromium.org>
Cr-Commit-Position: refs/heads/master@{#800591}
parent 173b1a40
...@@ -748,6 +748,7 @@ chrome_java_sources = [ ...@@ -748,6 +748,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/firstrun/TabbedModeFirstRunActivity.java", "java/src/org/chromium/chrome/browser/firstrun/TabbedModeFirstRunActivity.java",
"java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java", "java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java",
"java/src/org/chromium/chrome/browser/firstrun/TosAndUmaFirstRunFragmentWithEnterpriseSupport.java", "java/src/org/chromium/chrome/browser/firstrun/TosAndUmaFirstRunFragmentWithEnterpriseSupport.java",
"java/src/org/chromium/chrome/browser/firstrun/TosAndUmaFragmentView.java",
"java/src/org/chromium/chrome/browser/flags/ChromeSessionState.java", "java/src/org/chromium/chrome/browser/flags/ChromeSessionState.java",
"java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java", "java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java",
"java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java", "java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java",
......
...@@ -4,64 +4,64 @@ ...@@ -4,64 +4,64 @@
Use of this source code is governed by a BSD-style license that can be Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. found in the LICENSE file.
--> -->
<org.chromium.chrome.browser.firstrun.FirstRunView <!-- Most of the placement in this layout is controlled by TosAndUmaFragmentView#onMeasure. When changing the layout in this file, be sure to also check on the view object to see what is changing to avoid unexpected behavior. -->
<org.chromium.chrome.browser.firstrun.TosAndUmaFragmentView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<ScrollView <ScrollView
android:id="@+id/scroll_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginBottom="@dimen/fre_button_bar_height" android:layout_marginBottom="@dimen/fre_button_bar_height"
android:fillViewport="true"> android:fillViewport="true">
<!-- The orientation of this view is changed dynamically to give a nicer layout when in
landscape mode on devices with small screens. -->
<LinearLayout <LinearLayout
android:id="@+id/fre_main_layout" android:id="@+id/fre_main_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:animateLayoutChanges="true"
android:gravity="center_horizontal"> android:gravity="center_horizontal|center_vertical">
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="@dimen/fre_image_height"
android:layout_marginHorizontal="@dimen/fre_vertical_spacing"
android:layout_marginBottom="@dimen/fre_vertical_spacing"
android:importantForAccessibility="no"
android:src="@drawable/fre_product_logo" />
<TextView
android:id="@+id/title"
android:text="@string/fre_welcome"
style="@style/FreTitle" />
<!-- The orientation of this view is changed dynamically to give a nicer layout when in
landscape mode on devices with small screens. -->
<LinearLayout <LinearLayout
android:id="@+id/fre_image_and_content" android:id="@+id/fre_title_and_content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical">
android:gravity="center_horizontal"
android:layout_marginTop="@dimen/fre_vertical_spacing">
<ImageView <TextView
android:id="@+id/image" android:id="@+id/title"
android:layout_width="wrap_content" android:text="@string/fre_welcome"
android:layout_height="@dimen/fre_image_height" style="@style/FreTitle" />
tools:ignore="ContentDescription"
android:src="@drawable/fre_product_logo" />
<org.chromium.components.browser_ui.widget.LoadingView <org.chromium.components.browser_ui.widget.LoadingView
android:id="@+id/progress_spinner_large" android:id="@+id/progress_spinner_large"
style="@style/Widget.AppCompat.ProgressBar" style="@style/Widget.AppCompat.ProgressBar"
android:layout_marginTop="@dimen/fre_vertical_spacing" android:layout_gravity="center_horizontal"
android:layout_gravity="bottom|center_horizontal" android:layout_height="@dimen/fre_loading_spinner_size"
android:layout_width="48dp" android:layout_width="@dimen/fre_loading_spinner_size"
android:layout_height="48dp"
android:visibility="gone"/> android:visibility="gone"/>
<LinearLayout <LinearLayout
android:id="@+id/fre_content_wrapper" android:id="@+id/fre_content_wrapper"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1"
android:layout_marginTop="@dimen/fre_vertical_spacing" android:layout_marginTop="@dimen/fre_vertical_spacing"
android:layout_marginEnd="@dimen/fre_content_margin" android:layout_marginEnd="@dimen/fre_content_margin"
android:layout_marginStart="@dimen/fre_content_margin" android:gravity="bottom"
android:orientation="vertical" > android:orientation="vertical" >
<org.chromium.ui.widget.TextViewWithClickableSpans <org.chromium.ui.widget.TextViewWithClickableSpans
...@@ -69,7 +69,6 @@ ...@@ -69,7 +69,6 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/fre_vertical_spacing" android:layout_marginBottom="@dimen/fre_vertical_spacing"
android:gravity="center"
android:lineSpacingMultiplier="1.4" android:lineSpacingMultiplier="1.4"
android:textAppearance="@style/TextAppearance.TextMedium.Primary" /> android:textAppearance="@style/TextAppearance.TextMedium.Primary" />
...@@ -80,31 +79,51 @@ ...@@ -80,31 +79,51 @@
android:lineSpacingMultiplier="1.4" android:lineSpacingMultiplier="1.4"
android:text="@string/fre_send_report_check" android:text="@string/fre_send_report_check"
android:textAppearance="@style/TextAppearance.TextMedium.Primary" /> android:textAppearance="@style/TextAppearance.TextMedium.Primary" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
<!-- fre_button_bar_height = 52dp = layout_height + layout_marginBottom --> <!-- fre_button_bar_height = 62dp = layout_height + layout_marginVertical * 2 -->
<org.chromium.ui.widget.ButtonCompat <ImageView
android:id="@+id/terms_accept" android:id="@+id/shadow"
android:layout_width="match_parent"
android:layout_height="@dimen/action_bar_shadow_height"
android:layout_gravity="bottom"
android:layout_marginBottom="@dimen/fre_button_bar_height"
android:background="@drawable/modern_toolbar_shadow"
android:scaleY="-1"
android:visibility="gone"
android:importantForAccessibility="no" />
<FrameLayout
android:id="@+id/fre_bottom_group"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="10dp" android:layout_marginHorizontal="@dimen/fre_content_margin"
android:layout_gravity="bottom|center_horizontal" android:animateLayoutChanges="true">
android:paddingStart="@dimen/fre_button_padding"
android:paddingEnd="@dimen/fre_button_padding" <org.chromium.ui.widget.ButtonCompat
android:text="@string/fre_accept_continue" android:id="@+id/terms_accept"
style="@style/FilledButton.Flat" /> android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/fre_bottom_vertical_margin"
android:layout_marginBottom="@dimen/fre_bottom_vertical_margin"
android:layout_gravity="bottom|center_horizontal"
android:paddingStart="@dimen/fre_button_padding"
android:paddingEnd="@dimen/fre_button_padding"
android:text="@string/fre_accept_continue"
style="@style/FilledButton.Flat" />
<!-- Same location as the button; marginButtom is adjusted for the different size. --> <!-- Same location as the button; marginButtom is adjusted for the different size. -->
<ProgressBar <ProgressBar
android:id="@+id/progress_spinner" android:id="@+id/progress_spinner"
style="@style/Widget.AppCompat.ProgressBar" style="@style/Widget.AppCompat.ProgressBar"
android:layout_marginBottom="22dp" android:layout_marginBottom="22dp"
android:layout_gravity="bottom|center_horizontal" android:layout_gravity="bottom|center_horizontal"
android:layout_width="24dp" android:layout_width="24dp"
android:layout_height="24dp" /> android:layout_height="24dp" />
</FrameLayout>
<!-- Place holder for privacy disclosure. --> <!-- Place holder for privacy disclosure. -->
<TextView <TextView
...@@ -114,4 +133,4 @@ ...@@ -114,4 +133,4 @@
android:layout_gravity="bottom|center" android:layout_gravity="bottom|center"
android:text="@string/fre_browser_managed_by_organization" android:text="@string/fre_browser_managed_by_organization"
android:visibility="gone"/> android:visibility="gone"/>
</org.chromium.chrome.browser.firstrun.FirstRunView> </org.chromium.chrome.browser.firstrun.TosAndUmaFragmentView>
...@@ -148,11 +148,14 @@ ...@@ -148,11 +148,14 @@
<!-- First Run Experience dimensions --> <!-- First Run Experience dimensions -->
<dimen name="fre_content_margin">24dp</dimen> <dimen name="fre_content_margin">24dp</dimen>
<dimen name="fre_vertical_spacing">32dp</dimen> <dimen name="fre_vertical_spacing">32dp</dimen>
<dimen name="fre_button_bar_height">52dp</dimen> <dimen name="fre_landscape_top_padding">72dp</dimen>
<dimen name="fre_button_bar_height">62dp</dimen>
<dimen name="fre_bottom_vertical_margin">10dp</dimen>
<dimen name="fre_button_padding">12dp</dimen> <dimen name="fre_button_padding">12dp</dimen>
<dimen name="fre_margin">24dp</dimen> <dimen name="fre_margin">24dp</dimen>
<dimen name="fre_image_height">120dp</dimen> <dimen name="fre_image_height">110dp</dimen>
<dimen name="fre_tos_checkbox_padding">12dp</dimen> <dimen name="fre_tos_checkbox_padding">12dp</dimen>
<dimen name="fre_loading_spinner_size">48dp</dimen>
<!-- Account Signin dimensions --> <!-- Account Signin dimensions -->
<!-- The Account Signin page appears in the First Run Experience (amongst other places), so uses <!-- The Account Signin page appears in the First Run Experience (amongst other places), so uses
......
...@@ -191,7 +191,7 @@ public class ToSAndUMAFirstRunFragment extends Fragment implements FirstRunFragm ...@@ -191,7 +191,7 @@ public class ToSAndUMAFirstRunFragment extends Fragment implements FirstRunFragm
// Exposed methods for ToSAndUMACCTFirstRunFragment // Exposed methods for ToSAndUMACCTFirstRunFragment
protected void setTosAndUmaVisible(boolean isVisible) { protected void setTosAndUmaVisible(boolean isVisible) {
int visibility = isVisible ? View.VISIBLE : View.INVISIBLE; int visibility = isVisible ? View.VISIBLE : View.GONE;
mAcceptButton.setVisibility(visibility); mAcceptButton.setVisibility(visibility);
mTosAndPrivacy.setVisibility(visibility); mTosAndPrivacy.setVisibility(visibility);
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.firstrun;
import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import org.chromium.chrome.R;
/**
* Base view for fre_tosanduma.xml. This view may change child view placement when changing screen
* dimensions (e.g. on rotation).
*/
public class TosAndUmaFragmentView extends FrameLayout {
private ScrollView mScrollView;
private LinearLayout mMainLayout;
// The "title and content" contains the mTitle, mContentWrapper, and mLoadingSpinner that is
// visible when waiting for policy to be loaded.
private LinearLayout mTitleAndContent;
// The "content wrapper" contains the ToS text and the UMA check box.
private LinearLayout mContentWrapper;
// The "bottom group" contains the accept & continue button, and a small spinner that displays
// in its place when waiting for C++ to load before processing the FRE screen.
private FrameLayout mBottomGroup;
private View mTitle;
private View mLogo;
private View mLoadingSpinner;
private View mShadow;
private int mLastHeight;
private int mLastWidth;
// Spacing params
private int mVerticalSpacing;
private int mImageSize;
private int mLoadingSpinnerSize;
private int mLandscapeTopPadding;
private int mHeadlineSize;
private int mContentMargin;
private int mButtonBarHeight;
/**
* Constructor for inflating via XML.
*/
public TosAndUmaFragmentView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mScrollView = findViewById(R.id.scroll_view);
mMainLayout = findViewById(R.id.fre_main_layout);
mTitleAndContent = findViewById(R.id.fre_title_and_content);
mContentWrapper = findViewById(R.id.fre_content_wrapper);
mBottomGroup = findViewById(R.id.fre_bottom_group);
mTitle = findViewById(R.id.title);
mLogo = findViewById(R.id.image);
mLoadingSpinner = findViewById(R.id.progress_spinner_large);
mShadow = findViewById(R.id.shadow);
// Set up shadow.
mScrollView.getViewTreeObserver().addOnScrollChangedListener(this::updateShadowVisibility);
// Cache resource demensions that used in #onMeasure
mVerticalSpacing = getResources().getDimensionPixelSize(R.dimen.fre_vertical_spacing);
mImageSize = getResources().getDimensionPixelSize(R.dimen.fre_image_height);
mLoadingSpinnerSize =
getResources().getDimensionPixelSize(R.dimen.fre_loading_spinner_size);
mLandscapeTopPadding =
getResources().getDimensionPixelSize(R.dimen.fre_landscape_top_padding);
mHeadlineSize = getResources().getDimensionPixelSize(R.dimen.headline_size);
mContentMargin = getResources().getDimensionPixelSize(R.dimen.fre_content_margin);
mButtonBarHeight = getResources().getDimensionPixelSize(R.dimen.fre_button_bar_height);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// For an alternate layout in horizontal mode for screens of a certain size. These are why
// the padding is set manually.
// This assumes that view's layout_width is set to match_parent.
assert MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY;
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
// If the layout orientation does not change, there's no need to recalculate layout
// attributes.
if (width != mLastWidth || height != mLastHeight) {
mLastHeight = height;
mLastWidth = width;
boolean useWideScreenLayout = shouldUseWideScreen(width, height);
mMainLayout.setOrientation(
useWideScreenLayout ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL);
mTitleAndContent.setPaddingRelative(mTitleAndContent.getPaddingStart(),
getTitleAndContentLayoutTopPadding(useWideScreenLayout),
mTitleAndContent.getPaddingEnd(), mTitleAndContent.getPaddingBottom());
setLogoLayoutParams(useWideScreenLayout, height);
setTitleLayoutParams(useWideScreenLayout);
setSpinnerLayoutParams(useWideScreenLayout, width, height);
mContentWrapper.setVerticalGravity(
getContentLayoutVerticalGravity(useWideScreenLayout));
setContentLayoutParams(useWideScreenLayout);
setBottomGroupLayoutParams(useWideScreenLayout);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
updateShadowVisibility();
}
private boolean shouldUseWideScreen(int width, int height) {
return (height >= mImageSize + 2 * mButtonBarHeight) && (width > 1.5 * height);
}
private void updateShadowVisibility() {
if (mScrollView.canScrollVertically(1)) {
mShadow.setVisibility(VISIBLE);
mShadow.bringToFront();
} else {
mShadow.setVisibility(GONE);
}
}
private void setSpinnerLayoutParams(boolean useWideScreen, int width, int height) {
LinearLayout.LayoutParams spinnerParams =
(LinearLayout.LayoutParams) mLoadingSpinner.getLayoutParams();
// Adjust the spinner placement. If in portrait mode, the spinner is centered in the region
// below the title; If in wide screen mode, the spinner is placed in the center of
// the entire screen. In all scenarios, because we cannot get the exact size for headline,
// the spinner placement is approximately centered.
if (useWideScreen) {
int freImageWidth = mImageSize + mVerticalSpacing * 2;
int spinnerStartMargin =
Math.max(0, (width / 2) - freImageWidth - mLoadingSpinnerSize / 2);
int topContentHeight = mHeadlineSize + mLandscapeTopPadding;
int spinnerTopMargin =
Math.max(0, height / 2 - topContentHeight - mLoadingSpinnerSize / 2);
spinnerParams.gravity = Gravity.START;
spinnerParams.setMarginStart(spinnerStartMargin);
spinnerParams.topMargin = spinnerTopMargin;
} else {
// Calculate the estimated space below the title, which is centered in the overall
// content view.
int spaceBelowTitle = height / 2 - mHeadlineSize;
// Place the spinner in the middle of the remaining space;
int spinnerTopMargin =
Math.max(mVerticalSpacing, (spaceBelowTitle - mLoadingSpinnerSize) / 2);
spinnerParams.gravity = Gravity.CENTER_HORIZONTAL;
spinnerParams.setMarginStart(0);
spinnerParams.topMargin = spinnerTopMargin;
}
mLoadingSpinner.setLayoutParams(spinnerParams);
}
private void setLogoLayoutParams(boolean useWideScreen, int height) {
LinearLayout.LayoutParams logoLayoutParams =
(LinearLayout.LayoutParams) mLogo.getLayoutParams();
if (useWideScreen) {
// When using the wide screen layout, we want to vertically center the logo on the start
// side of the screen. While we have no padding on the main layout when using the wide
// screen, we'll calculate the space needed and set it as top margin above the logo to
// make it centered.
int topMargin = (height - mImageSize) / 2;
// We only use the wide screen layout when the screen height is tall enough to
// accommodate the image and some padding. But just in case that calculation fails,
// ensure topMargin isn't negative.
assert topMargin > 0;
logoLayoutParams.topMargin = Math.max(0, topMargin);
logoLayoutParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
} else {
// Otherwise, in tall screen mode, we want the image to sit right above the title
// with a vertical spacing in between. In XML, the title is ordered below the logo in
// the containing linear layout, so if we align the bottom of the logo mVerticalSpacing
// above the center of the screen, the top of the title will be at the center of the
// screen. While calculation is done in a similar way, we are putting
// mVerticalSpacing for marginTop as minimum to avoid 0dp spacing between top and logo
// on small screen devices.
int freImageHeight = mImageSize + mVerticalSpacing;
logoLayoutParams.topMargin = Math.max(mVerticalSpacing, (height / 2 - freImageHeight));
logoLayoutParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
}
}
private void setTitleLayoutParams(boolean useWideScreen) {
LinearLayout.LayoutParams titleParams =
(LinearLayout.LayoutParams) mTitle.getLayoutParams();
titleParams.gravity = useWideScreen ? Gravity.START : Gravity.CENTER;
}
private void setContentLayoutParams(boolean useWideScreen) {
MarginLayoutParams contentWrapperLayoutParams =
(MarginLayoutParams) mContentWrapper.getLayoutParams();
contentWrapperLayoutParams.setMarginStart(useWideScreen ? 0 : mContentMargin);
mContentWrapper.setLayoutParams(contentWrapperLayoutParams);
}
private void setBottomGroupLayoutParams(boolean useWideScreen) {
FrameLayout.LayoutParams bottomGroupParams =
(FrameLayout.LayoutParams) mBottomGroup.getLayoutParams();
bottomGroupParams.gravity = useWideScreen ? Gravity.END | Gravity.BOTTOM
: Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
}
private int getContentLayoutVerticalGravity(boolean useWideScreen) {
return useWideScreen ? Gravity.CENTER_VERTICAL : Gravity.BOTTOM;
}
private int getTitleAndContentLayoutTopPadding(boolean useWideScreen) {
return useWideScreen ? mLandscapeTopPadding : 0;
}
}
...@@ -254,8 +254,7 @@ public class TosAndUmaFirstRunFragmentWithEnterpriseSupportTest { ...@@ -254,8 +254,7 @@ public class TosAndUmaFirstRunFragmentWithEnterpriseSupportTest {
} }
private void assertUIState(@FragmentState int fragmentState) { private void assertUIState(@FragmentState int fragmentState) {
int tosVisibility = int tosVisibility = (fragmentState == FragmentState.NO_POLICY) ? View.VISIBLE : View.GONE;
(fragmentState == FragmentState.NO_POLICY) ? View.VISIBLE : View.INVISIBLE;
int spinnerVisibility = (fragmentState == FragmentState.LOADING) ? View.VISIBLE : View.GONE; int spinnerVisibility = (fragmentState == FragmentState.LOADING) ? View.VISIBLE : View.GONE;
CriteriaHelper.pollUiThread( CriteriaHelper.pollUiThread(
......
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