Commit 453b6ffe authored by Wenyu Fu's avatar Wenyu Fu Committed by Commit Bot

[CCTToSFRE] Add privacy disclaimer when ToS is skipped

Add a privacy disclosure when ToS is skipped by policy in the FRE. The
privacy disclaimer will be displayed on screen for 1 second before FRE
is finished.

Bug: 1108564, 1128158
Change-Id: I5f912e62484a5255bfda27db0318c0e92d541903
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2548293
Commit-Queue: Wenyu Fu <wenyufu@chromium.org>
Reviewed-by: default avatarSky Malice <skym@chromium.org>
Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#830685}
parent 3e345e0a
...@@ -851,6 +851,7 @@ chrome_java_resources = [ ...@@ -851,6 +851,7 @@ chrome_java_resources = [
"java/res/layout/find_in_page.xml", "java/res/layout/find_in_page.xml",
"java/res/layout/find_toolbar.xml", "java/res/layout/find_toolbar.xml",
"java/res/layout/fre_data_reduction_proxy_lite_mode.xml", "java/res/layout/fre_data_reduction_proxy_lite_mode.xml",
"java/res/layout/fre_tos_privacy_disclaimer.xml",
"java/res/layout/fre_tosanduma.xml", "java/res/layout/fre_tosanduma.xml",
"java/res/layout/history_clear_browsing_data_header.xml", "java/res/layout/history_clear_browsing_data_header.xml",
"java/res/layout/history_item_view.xml", "java/res/layout/history_item_view.xml",
......
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<org.chromium.components.browser_ui.widget.text.TextViewWithCompoundDrawables
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/privacy_disclaimer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="@dimen/fre_policy_privacy_disclaimer_icon_padding"
android:drawableStart="@drawable/ic_business"
android:text="@string/fre_browser_managed_by_organization"
android:textAppearance="@style/TextAppearance.TextSmall.Secondary"
app:chromeDrawableTint="@color/default_text_color_secondary"
app:drawableWidth="@dimen/fre_policy_privacy_disclaimer_icon_size"
app:drawableHeight="@dimen/fre_policy_privacy_disclaimer_icon_size"
tools:showIn="@layout/lightweight_fre_tos" />
\ No newline at end of file
...@@ -93,6 +93,14 @@ ...@@ -93,6 +93,14 @@
android:text="@string/fre_send_report_check" android:text="@string/fre_send_report_check"
android:paddingStart="@dimen/fre_tos_checkbox_padding" android:paddingStart="@dimen/fre_tos_checkbox_padding"
android:textAppearance="@style/TextAppearance.TextMedium.Primary" /> android:textAppearance="@style/TextAppearance.TextMedium.Primary" />
<include
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/fre_content_margin"
android:layout_marginBottom="@dimen/fre_vertical_spacing"
android:visibility="gone"
layout="@layout/fre_tos_privacy_disclaimer" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
...@@ -137,15 +145,6 @@ ...@@ -137,15 +145,6 @@
android:layout_height="@dimen/fre_bottom_loading_spinner_size"/> android:layout_height="@dimen/fre_bottom_loading_spinner_size"/>
</FrameLayout> </FrameLayout>
<!-- Place holder for privacy disclosure. -->
<TextView
android:id="@+id/privacy_disclaimer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center"
android:text="@string/fre_browser_managed_by_organization"
android:visibility="gone"/>
<!-- Empty TextView to preload fonts for following pages. See https://crbug.com/1119990#c20 --> <!-- Empty TextView to preload fonts for following pages. See https://crbug.com/1119990#c20 -->
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
......
...@@ -58,6 +58,14 @@ ...@@ -58,6 +58,14 @@
android:layout_width="@dimen/fre_loading_spinner_size" android:layout_width="@dimen/fre_loading_spinner_size"
android:visibility="gone"/> android:visibility="gone"/>
</FrameLayout> </FrameLayout>
<include
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:visibility="gone"
layout="@layout/fre_tos_privacy_disclaimer" />
</FrameLayout> </FrameLayout>
<org.chromium.components.browser_ui.widget.DualControlLayout <org.chromium.components.browser_ui.widget.DualControlLayout
......
...@@ -157,6 +157,8 @@ ...@@ -157,6 +157,8 @@
<dimen name="fre_loading_spinner_size">48dp</dimen> <dimen name="fre_loading_spinner_size">48dp</dimen>
<dimen name="fre_button_padding">12dp</dimen> <dimen name="fre_button_padding">12dp</dimen>
<dimen name="fre_bottom_loading_spinner_size">24dp</dimen> <dimen name="fre_bottom_loading_spinner_size">24dp</dimen>
<dimen name="fre_policy_privacy_disclaimer_icon_size">18dp</dimen>
<dimen name="fre_policy_privacy_disclaimer_icon_padding">8dp</dimen>
<dimen name="fre_button_vertical_margin">24dp</dimen> <dimen name="fre_button_vertical_margin">24dp</dimen>
<dimen name="fre_button_bar_height">62dp</dimen> <dimen name="fre_button_bar_height">62dp</dimen>
......
...@@ -22,6 +22,7 @@ import org.chromium.components.signin.AccountManagerFacadeProvider; ...@@ -22,6 +22,7 @@ import org.chromium.components.signin.AccountManagerFacadeProvider;
/** Provides first run related utility functions. */ /** Provides first run related utility functions. */
public class FirstRunUtils { public class FirstRunUtils {
private static Boolean sHasGoogleAccountAuthenticator; private static Boolean sHasGoogleAccountAuthenticator;
static final int SKIP_TOS_EXIT_DELAY_MS = 1000;
/** /**
* Synchronizes first run native and Java preferences. * Synchronizes first run native and Java preferences.
......
...@@ -6,6 +6,7 @@ package org.chromium.chrome.browser.firstrun; ...@@ -6,6 +6,7 @@ package org.chromium.chrome.browser.firstrun;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock; import android.os.SystemClock;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater; import android.view.LayoutInflater;
...@@ -19,6 +20,7 @@ import androidx.annotation.StringRes; ...@@ -19,6 +20,7 @@ import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import org.chromium.base.IntentUtils; import org.chromium.base.IntentUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.supplier.OneshotSupplierImpl; import org.chromium.base.supplier.OneshotSupplierImpl;
import org.chromium.chrome.R; import org.chromium.chrome.R;
...@@ -52,12 +54,16 @@ public class LightweightFirstRunActivity ...@@ -52,12 +54,16 @@ public class LightweightFirstRunActivity
private LoadingView mLoadingView; private LoadingView mLoadingView;
private View mLoadingViewContainer; private View mLoadingViewContainer;
private View mLightweightFreButtons; private View mLightweightFreButtons;
private View mPrivacyDisclaimer;
private boolean mViewCreated; private boolean mViewCreated;
private boolean mNativeInitialized; private boolean mNativeInitialized;
private boolean mTriggerAcceptAfterNativeInit; private boolean mTriggerAcceptAfterNativeInit;
private long mViewCreatedTimeMs; private long mViewCreatedTimeMs;
private Handler mHandler;
private Runnable mExitFreRunnable;
public static final String EXTRA_ASSOCIATED_APP_NAME = public static final String EXTRA_ASSOCIATED_APP_NAME =
"org.chromium.chrome.browser.firstrun.AssociatedAppName"; "org.chromium.chrome.browser.firstrun.AssociatedAppName";
...@@ -147,6 +153,8 @@ public class LightweightFirstRunActivity ...@@ -147,6 +153,8 @@ public class LightweightFirstRunActivity
mLoadingView = findViewById(R.id.loading_view); mLoadingView = findViewById(R.id.loading_view);
mLoadingViewContainer = findViewById(R.id.loading_view_container); mLoadingViewContainer = findViewById(R.id.loading_view_container);
mPrivacyDisclaimer = findViewById(R.id.privacy_disclaimer);
mViewCreated = true; mViewCreated = true;
mViewCreatedTimeMs = SystemClock.elapsedRealtime(); mViewCreatedTimeMs = SystemClock.elapsedRealtime();
...@@ -227,6 +235,10 @@ public class LightweightFirstRunActivity ...@@ -227,6 +235,10 @@ public class LightweightFirstRunActivity
// As first run is complete, we no longer need FirstRunAppRestrictionInfo. // As first run is complete, we no longer need FirstRunAppRestrictionInfo.
if (mFirstRunAppRestrictionInfo != null) mFirstRunAppRestrictionInfo.destroy(); if (mFirstRunAppRestrictionInfo != null) mFirstRunAppRestrictionInfo.destroy();
if (mSkipTosDialogPolicyListener != null) mSkipTosDialogPolicyListener.destroy(); if (mSkipTosDialogPolicyListener != null) mSkipTosDialogPolicyListener.destroy();
if (mHandler != null && mExitFreRunnable != null) {
mHandler.removeCallbacks(mExitFreRunnable);
}
} }
private void abortFirstRunExperience() { private void abortFirstRunExperience() {
...@@ -240,9 +252,17 @@ public class LightweightFirstRunActivity ...@@ -240,9 +252,17 @@ public class LightweightFirstRunActivity
} }
private void skipTosByPolicy() { private void skipTosByPolicy() {
// TODO(crbug.com/1108564): Show the different UI that has the enterprise disclosure. mLoadingViewContainer.setVisibility(View.GONE);
FirstRunStatus.setEphemeralSkipFirstRun(true); mPrivacyDisclaimer.setVisibility(View.VISIBLE);
exitLightweightFirstRun(); mPrivacyDisclaimer.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
mExitFreRunnable = () -> {
FirstRunStatus.setEphemeralSkipFirstRun(true);
exitLightweightFirstRun();
mExitFreRunnable = null;
};
mHandler = new Handler(ThreadUtils.getUiThreadLooper());
mHandler.postDelayed(mExitFreRunnable, FirstRunUtils.SKIP_TOS_EXIT_DELAY_MS);
} }
private void exitLightweightFirstRun() { private void exitLightweightFirstRun() {
......
...@@ -6,6 +6,7 @@ package org.chromium.chrome.browser.firstrun; ...@@ -6,6 +6,7 @@ package org.chromium.chrome.browser.firstrun;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock; import android.os.SystemClock;
import android.view.View; import android.view.View;
import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEvent;
...@@ -13,6 +14,7 @@ import android.view.accessibility.AccessibilityEvent; ...@@ -13,6 +14,7 @@ import android.view.accessibility.AccessibilityEvent;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.chromium.base.Log; import org.chromium.base.Log;
import org.chromium.base.ThreadUtils;
import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.supplier.OneshotSupplierImpl; import org.chromium.base.supplier.OneshotSupplierImpl;
import org.chromium.chrome.R; import org.chromium.chrome.R;
...@@ -64,10 +66,14 @@ public class TosAndUmaFirstRunFragmentWithEnterpriseSupport ...@@ -64,10 +66,14 @@ public class TosAndUmaFirstRunFragmentWithEnterpriseSupport
private boolean mViewCreated; private boolean mViewCreated;
private View mLoadingSpinnerContainer; private View mLoadingSpinnerContainer;
private LoadingView mLoadingSpinner; private LoadingView mLoadingSpinner;
private View mPrivacyDisclaimer;
private SkipTosDialogPolicyListener mSkipTosDialogPolicyListener; private SkipTosDialogPolicyListener mSkipTosDialogPolicyListener;
private final OneshotSupplierImpl<PolicyService> mPolicyServiceProvider = private final OneshotSupplierImpl<PolicyService> mPolicyServiceProvider =
new OneshotSupplierImpl<>(); new OneshotSupplierImpl<>();
private Handler mHandler;
private Runnable mExitFreRunnable;
/** The {@link SystemClock} timestamp when onViewCreated is called. */ /** The {@link SystemClock} timestamp when onViewCreated is called. */
private long mViewCreatedTimeMs; private long mViewCreatedTimeMs;
...@@ -81,6 +87,10 @@ public class TosAndUmaFirstRunFragmentWithEnterpriseSupport ...@@ -81,6 +87,10 @@ public class TosAndUmaFirstRunFragmentWithEnterpriseSupport
mSkipTosDialogPolicyListener.destroy(); mSkipTosDialogPolicyListener.destroy();
mSkipTosDialogPolicyListener = null; mSkipTosDialogPolicyListener = null;
} }
if (mHandler != null && mExitFreRunnable != null) {
mHandler.removeCallbacks(mExitFreRunnable);
mHandler = null;
}
super.onDestroy(); super.onDestroy();
} }
...@@ -101,6 +111,7 @@ public class TosAndUmaFirstRunFragmentWithEnterpriseSupport ...@@ -101,6 +111,7 @@ public class TosAndUmaFirstRunFragmentWithEnterpriseSupport
mLoadingSpinnerContainer = view.findViewById(R.id.loading_view_container); mLoadingSpinnerContainer = view.findViewById(R.id.loading_view_container);
mLoadingSpinner = view.findViewById(R.id.progress_spinner_large); mLoadingSpinner = view.findViewById(R.id.progress_spinner_large);
mPrivacyDisclaimer = view.findViewById(R.id.privacy_disclaimer);
mViewCreated = true; mViewCreated = true;
mViewCreatedTimeMs = SystemClock.elapsedRealtime(); mViewCreatedTimeMs = SystemClock.elapsedRealtime();
...@@ -142,7 +153,6 @@ public class TosAndUmaFirstRunFragmentWithEnterpriseSupport ...@@ -142,7 +153,6 @@ public class TosAndUmaFirstRunFragmentWithEnterpriseSupport
SystemClock.elapsedRealtime() - mViewCreatedTimeMs); SystemClock.elapsedRealtime() - mViewCreatedTimeMs);
if (mSkipTosDialogPolicyListener.get()) { if (mSkipTosDialogPolicyListener.get()) {
// TODO(crbug.com/1108564): Show the different UI that has the enterprise disclosure.
exitCctFirstRun(); exitCctFirstRun();
} else { } else {
// Else, show the UMA as the loading spinner is GONE. // Else, show the UMA as the loading spinner is GONE.
...@@ -161,10 +171,18 @@ public class TosAndUmaFirstRunFragmentWithEnterpriseSupport ...@@ -161,10 +171,18 @@ public class TosAndUmaFirstRunFragmentWithEnterpriseSupport
} }
private void exitCctFirstRun() { private void exitCctFirstRun() {
// TODO(crbug.com/1108564): Fire a signal to end this fragment when disclaimer is ready.
// TODO(crbug.com/1108582): Save a shared pref indicating Enterprise CCT FRE is complete, // TODO(crbug.com/1108582): Save a shared pref indicating Enterprise CCT FRE is complete,
// and skip waiting for future cold starts. // and skip waiting for future cold starts.
Log.d(TAG, "TosAndUmaFirstRunFragmentWithEnterpriseSupport finished."); Log.d(TAG, "TosAndUmaFirstRunFragmentWithEnterpriseSupport finished.");
getPageDelegate().exitFirstRun();
mPrivacyDisclaimer.setVisibility(View.VISIBLE);
mPrivacyDisclaimer.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
mExitFreRunnable = () -> {
getPageDelegate().exitFirstRun();
mExitFreRunnable = null;
};
mHandler = new Handler(ThreadUtils.getUiThreadLooper());
mHandler.postDelayed(mExitFreRunnable, FirstRunUtils.SKIP_TOS_EXIT_DELAY_MS);
} }
} }
...@@ -17,6 +17,8 @@ import org.chromium.chrome.R; ...@@ -17,6 +17,8 @@ import org.chromium.chrome.R;
/** /**
* Base view for fre_tosanduma.xml. This view may change child view placement when changing screen * Base view for fre_tosanduma.xml. This view may change child view placement when changing screen
* dimensions (e.g. on rotation). * dimensions (e.g. on rotation).
*
* See https://crbug.com/1151537 for illustration.
*/ */
public class TosAndUmaFragmentView extends FrameLayout { public class TosAndUmaFragmentView extends FrameLayout {
private ScrollView mScrollView; private ScrollView mScrollView;
......
...@@ -113,6 +113,7 @@ public class TosAndUmaFirstRunFragmentWithEnterpriseSupportTest { ...@@ -113,6 +113,7 @@ public class TosAndUmaFirstRunFragmentWithEnterpriseSupportTest {
private View mTosText; private View mTosText;
private View mAcceptButton; private View mAcceptButton;
private View mLargeSpinner; private View mLargeSpinner;
private View mPrivacyDisclaimer;
private CheckBox mUmaCheckBox; private CheckBox mUmaCheckBox;
@Before @Before
...@@ -425,6 +426,29 @@ public class TosAndUmaFirstRunFragmentWithEnterpriseSupportTest { ...@@ -425,6 +426,29 @@ public class TosAndUmaFirstRunFragmentWithEnterpriseSupportTest {
renderWithPortraitAndLandscape(tosAndUmaFragment, "fre_tosanduma_nopolicy"); renderWithPortraitAndLandscape(tosAndUmaFragment, "fre_tosanduma_nopolicy");
} }
@Test
@SmallTest
@Feature({"RenderTest", "FirstRun"})
public void testRenderWithPolicy() throws Exception {
setAppRestrictionsMockInitialized(true);
setEnterpriseInfoInitializedWithDeviceOwner(true);
launchFirstRunThroughCustomTab();
assertUIState(FragmentState.LOADING);
// Clear the focus on view to avoid unexpected highlight on background.
View tosAndUmaFragment =
mActivity.getSupportFragmentManager().getFragments().get(0).getView();
Assert.assertNotNull(tosAndUmaFragment);
TestThreadUtils.runOnUiThreadBlocking(tosAndUmaFragment::clearFocus);
setPolicyServiceMockInitializedWithDialogEnabled(false);
CriteriaHelper.pollUiThread(
()
-> Criteria.checkThat(
mPrivacyDisclaimer.getVisibility(), Matchers.is(View.VISIBLE)));
renderWithPortraitAndLandscape(tosAndUmaFragment, "fre_tosanduma_withpolicy");
}
/** /**
* Launch chrome through custom tab and trigger first run. * Launch chrome through custom tab and trigger first run.
*/ */
...@@ -462,11 +486,14 @@ public class TosAndUmaFirstRunFragmentWithEnterpriseSupportTest { ...@@ -462,11 +486,14 @@ public class TosAndUmaFirstRunFragmentWithEnterpriseSupportTest {
mUmaCheckBox = mActivity.findViewById(R.id.send_report_checkbox); mUmaCheckBox = mActivity.findViewById(R.id.send_report_checkbox);
mAcceptButton = mActivity.findViewById(R.id.terms_accept); mAcceptButton = mActivity.findViewById(R.id.terms_accept);
mLargeSpinner = mActivity.findViewById(R.id.progress_spinner_large); mLargeSpinner = mActivity.findViewById(R.id.progress_spinner_large);
mPrivacyDisclaimer = mActivity.findViewById(R.id.privacy_disclaimer);
} }
private void assertUIState(@FragmentState int fragmentState) { private void assertUIState(@FragmentState int fragmentState) {
int tosVisibility = (fragmentState == FragmentState.NO_POLICY) ? View.VISIBLE : View.GONE; int tosVisibility = (fragmentState == FragmentState.NO_POLICY) ? View.VISIBLE : View.GONE;
int spinnerVisibility = (fragmentState == FragmentState.LOADING) ? View.VISIBLE : View.GONE; int spinnerVisibility = (fragmentState == FragmentState.LOADING) ? View.VISIBLE : View.GONE;
int privacyVisibility =
(fragmentState == FragmentState.HAS_POLICY) ? View.VISIBLE : View.GONE;
CriteriaHelper.pollUiThread( CriteriaHelper.pollUiThread(
() ()
...@@ -480,11 +507,14 @@ public class TosAndUmaFirstRunFragmentWithEnterpriseSupportTest { ...@@ -480,11 +507,14 @@ public class TosAndUmaFirstRunFragmentWithEnterpriseSupportTest {
tosVisibility, mUmaCheckBox.getVisibility()); tosVisibility, mUmaCheckBox.getVisibility());
Assert.assertEquals("Visibility of accept button is different than the test setting.", Assert.assertEquals("Visibility of accept button is different than the test setting.",
tosVisibility, mAcceptButton.getVisibility()); tosVisibility, mAcceptButton.getVisibility());
Assert.assertEquals("Visibility of privacy disclaimer is different than the test setting.",
privacyVisibility, mPrivacyDisclaimer.getVisibility());
Assert.assertTrue("Uma Check Box should be checked.", mUmaCheckBox.isChecked()); Assert.assertTrue("Uma Check Box should be checked.", mUmaCheckBox.isChecked());
int expectedExitCount = fragmentState == FragmentState.HAS_POLICY ? 1 : 0; int expectedExitCount = fragmentState == FragmentState.HAS_POLICY ? 1 : 0;
Assert.assertEquals(expectedExitCount, mExitCount); CriteriaHelper.pollUiThread(
() -> Criteria.checkThat(mExitCount, Matchers.is(expectedExitCount)));
} }
/** /**
......
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