|
@@ -0,0 +1,716 @@
|
|
|
+From: csagan5 <32685696+csagan5@users.noreply.github.com>
|
|
|
+Date: Fri, 29 Apr 2022 00:31:49 +0200
|
|
|
+Subject: Welcome screen
|
|
|
+
|
|
|
+Allow toggling automatic updates
|
|
|
+---
|
|
|
+ .../android/java/res/layout/fre_tosanduma.xml | 4 +-
|
|
|
+ .../browser/firstrun/FirstRunActivity.java | 58 +------
|
|
|
+ .../firstrun/FirstRunFlowSequencer.java | 81 ++-------
|
|
|
+ .../browser/firstrun/FirstRunUtils.java | 13 +-
|
|
|
+ .../firstrun/ToSAndUMAFirstRunFragment.java | 163 +++++-------------
|
|
|
+ .../strings/android_chrome_strings.grd | 27 ++-
|
|
|
+ 6 files changed, 96 insertions(+), 250 deletions(-)
|
|
|
+
|
|
|
+diff --git a/chrome/android/java/res/layout/fre_tosanduma.xml b/chrome/android/java/res/layout/fre_tosanduma.xml
|
|
|
+--- a/chrome/android/java/res/layout/fre_tosanduma.xml
|
|
|
++++ b/chrome/android/java/res/layout/fre_tosanduma.xml
|
|
|
+@@ -86,11 +86,11 @@
|
|
|
+ android:textAppearance="@style/TextAppearance.TextMedium.Primary" />
|
|
|
+
|
|
|
+ <CheckBox
|
|
|
+- android:id="@+id/send_report_checkbox"
|
|
|
++ android:id="@+id/auto_updater_checkbox"
|
|
|
+ android:layout_width="wrap_content"
|
|
|
+ android:layout_height="wrap_content"
|
|
|
+ android:lineSpacingMultiplier="1.4"
|
|
|
+- android:text="@string/fre_send_report_check"
|
|
|
++ android:text="@string/auto_updater_check"
|
|
|
+ android:paddingStart="@dimen/fre_tos_checkbox_padding"
|
|
|
+ android:textAppearance="@style/TextAppearance.TextMedium.Primary" />
|
|
|
+ </LinearLayout>
|
|
|
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
|
|
|
+--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
|
|
|
++++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
|
|
|
+@@ -31,8 +31,6 @@ import org.chromium.chrome.browser.customtabs.CustomTabActivity;
|
|
|
+ import org.chromium.chrome.browser.fonts.FontPreloader;
|
|
|
+ import org.chromium.chrome.browser.metrics.UmaUtils;
|
|
|
+ import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
|
|
|
+-import org.chromium.chrome.browser.signin.SigninFirstRunFragment;
|
|
|
+-import org.chromium.chrome.browser.signin.services.FREMobileIdentityConsistencyFieldTrial;
|
|
|
+ import org.chromium.components.browser_ui.modaldialog.AppModalPresenter;
|
|
|
+ import org.chromium.ui.base.LocalizationUtils;
|
|
|
+ import org.chromium.ui.modaldialog.ModalDialogManager;
|
|
|
+@@ -96,8 +94,6 @@ public class FirstRunActivity extends FirstRunActivityBase implements FirstRunPa
|
|
|
+ @Nullable
|
|
|
+ private static FirstRunActivityObserver sObserver;
|
|
|
+
|
|
|
+- private String mResultSyncConsentAccountName;
|
|
|
+-
|
|
|
+ private boolean mPostNativeAndPolicyPagesCreated;
|
|
|
+ // Use hasValue() to simplify access. Will be null before initialized.
|
|
|
+ private final OneshotSupplierImpl<Boolean> mNativeSideIsInitializedSupplier =
|
|
|
+@@ -146,18 +142,7 @@ public class FirstRunActivity extends FirstRunActivityBase implements FirstRunPa
|
|
|
+ /** Creates first page and sets up adapter. Should result UI being shown on the screen. */
|
|
|
+ private void createFirstPage() {
|
|
|
+ BooleanSupplier showWelcomePage = () -> !FirstRunStatus.shouldSkipWelcomePage();
|
|
|
+- if (FREMobileIdentityConsistencyFieldTrial.isEnabled()) {
|
|
|
+- mPages.add(new FirstRunPage<>(SigninFirstRunFragment.class, showWelcomePage));
|
|
|
+- } else {
|
|
|
+- // TODO(crbug.com/1111490): Revisit during post-MVP.
|
|
|
+- // There's an edge case where we accept the welcome page in the main app, abort the FRE,
|
|
|
+- // then go through this CCT FRE again.
|
|
|
+- mPages.add(shouldCreateEnterpriseCctTosPage()
|
|
|
+- ? new FirstRunPage<>(
|
|
|
+- TosAndUmaFirstRunFragmentWithEnterpriseSupport.class,
|
|
|
+- showWelcomePage)
|
|
|
+- : new FirstRunPage<>(ToSAndUMAFirstRunFragment.class, showWelcomePage));
|
|
|
+- }
|
|
|
++ mPages.add(new FirstRunPage<>(ToSAndUMAFirstRunFragment.class, showWelcomePage));
|
|
|
+ mFreProgressStates.add(MobileFreProgress.WELCOME_SHOWN);
|
|
|
+ mPagerAdapter = new FirstRunPagerAdapter(FirstRunActivity.this, mPages);
|
|
|
+ mPager.setAdapter(mPagerAdapter);
|
|
|
+@@ -165,15 +150,6 @@ public class FirstRunActivity extends FirstRunActivityBase implements FirstRunPa
|
|
|
+ // native and policy service have been initialized.
|
|
|
+ }
|
|
|
+
|
|
|
+- private boolean shouldCreateEnterpriseCctTosPage() {
|
|
|
+- // TODO(crbug.com/1111490): Revisit case when #shouldSkipWelcomePage = true.
|
|
|
+- // If the client has already accepted ToS (FirstRunStatus#shouldSkipWelcomePage), do not
|
|
|
+- // use the subclass ToSAndUmaCCTFirstRunFragment. Instead, use the base class
|
|
|
+- // (ToSAndUMAFirstRunFragment) which simply shows a loading spinner while waiting for
|
|
|
+- // native to be loaded.
|
|
|
+- return mLaunchedFromCCT && !FirstRunStatus.shouldSkipWelcomePage();
|
|
|
+- }
|
|
|
+-
|
|
|
+ /**
|
|
|
+ * Create the page sequence which requires native initialized, and policies loaded if any
|
|
|
+ * on-device policies may exists.
|
|
|
+@@ -187,7 +163,6 @@ public class FirstRunActivity extends FirstRunActivityBase implements FirstRunPa
|
|
|
+
|
|
|
+ BooleanSupplier showSearchEnginePromo =
|
|
|
+ () -> mFreProperties.getBoolean(SHOW_SEARCH_ENGINE_PAGE);
|
|
|
+- BooleanSupplier showSyncConsent = () -> mFreProperties.getBoolean(SHOW_SYNC_CONSENT_PAGE);
|
|
|
+
|
|
|
+ // An optional page to select a default search engine.
|
|
|
+ if (showSearchEnginePromo.getAsBoolean()) {
|
|
|
+@@ -196,11 +171,6 @@ public class FirstRunActivity extends FirstRunActivityBase implements FirstRunPa
|
|
|
+ mFreProgressStates.add(MobileFreProgress.DEFAULT_SEARCH_ENGINE_SHOWN);
|
|
|
+ }
|
|
|
+
|
|
|
+- // An optional sync consent page, the visibility of this page will be decided on the fly
|
|
|
+- // according to the situation.
|
|
|
+- mPages.add(new FirstRunPage<>(SyncConsentFirstRunFragment.class, showSyncConsent));
|
|
|
+- mFreProgressStates.add(MobileFreProgress.SYNC_CONSENT_SHOWN);
|
|
|
+-
|
|
|
+ if (mPagerAdapter != null) {
|
|
|
+ mPagerAdapter.notifyDataSetChanged();
|
|
|
+ }
|
|
|
+@@ -245,10 +215,6 @@ public class FirstRunActivity extends FirstRunActivityBase implements FirstRunPa
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void triggerLayoutInflation() {
|
|
|
+- // Generate trial group as early as possible to guarantee it's available by the time native
|
|
|
+- // needs to register the synthetic trial group. See https://crbug.com/1295692 for details.
|
|
|
+- FREMobileIdentityConsistencyFieldTrial.createFirstRunTrial();
|
|
|
+-
|
|
|
+ initializeStateFromLaunchData();
|
|
|
+ RecordHistogram.recordTimesHistogram("MobileFre.FromLaunch.TriggerLayoutInflation",
|
|
|
+ SystemClock.elapsedRealtime() - mIntentCreationElapsedRealtimeMs);
|
|
|
+@@ -256,8 +222,12 @@ public class FirstRunActivity extends FirstRunActivityBase implements FirstRunPa
|
|
|
+ setFinishOnTouchOutside(true);
|
|
|
+
|
|
|
+ setContentView(createContentView());
|
|
|
+- ViewDrawBlocker.blockViewDrawUntilReady(
|
|
|
++ if (mPagerAdapter == null) {
|
|
|
++ createFirstPage();
|
|
|
++ } else {
|
|
|
++ ViewDrawBlocker.blockViewDrawUntilReady(
|
|
|
+ findViewById(android.R.id.content), () -> mPages.size() > 0);
|
|
|
++ }
|
|
|
+
|
|
|
+ mFirstRunFlowSequencer = new FirstRunFlowSequencer(this) {
|
|
|
+ @Override
|
|
|
+@@ -462,12 +432,8 @@ public class FirstRunActivity extends FirstRunActivityBase implements FirstRunPa
|
|
|
+ public void completeFirstRunExperience() {
|
|
|
+ RecordHistogram.recordMediumTimesHistogram("MobileFre.FromLaunch.FreCompleted",
|
|
|
+ SystemClock.elapsedRealtime() - mIntentCreationElapsedRealtimeMs);
|
|
|
+- if (mFreProperties.getBoolean(OPEN_ADVANCED_SYNC_SETTINGS)) {
|
|
|
+- recordFreProgressHistogram(MobileFreProgress.SYNC_CONSENT_SETTINGS_LINK_CLICK);
|
|
|
+- }
|
|
|
+
|
|
|
+- FirstRunFlowSequencer.markFlowAsCompleted(mResultSyncConsentAccountName,
|
|
|
+- mFreProperties.getBoolean(OPEN_ADVANCED_SYNC_SETTINGS));
|
|
|
++ FirstRunFlowSequencer.markFlowAsCompleted();
|
|
|
+
|
|
|
+ if (sObserver != null) sObserver.onUpdateCachedEngineName(this);
|
|
|
+
|
|
|
+@@ -510,16 +476,10 @@ public class FirstRunActivity extends FirstRunActivityBase implements FirstRunPa
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void refuseSync() {
|
|
|
+- mResultSyncConsentAccountName = null;
|
|
|
+- mFreProperties.putBoolean(OPEN_ADVANCED_SYNC_SETTINGS, false);
|
|
|
+- recordFreProgressHistogram(MobileFreProgress.SYNC_CONSENT_DISMISSED);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void acceptSync(String accountName, boolean openSettings) {
|
|
|
+- mResultSyncConsentAccountName = accountName;
|
|
|
+- mFreProperties.putBoolean(OPEN_ADVANCED_SYNC_SETTINGS, openSettings);
|
|
|
+- recordFreProgressHistogram(MobileFreProgress.SYNC_CONSENT_ACCEPTED);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+@@ -536,10 +496,6 @@ public class FirstRunActivity extends FirstRunActivityBase implements FirstRunPa
|
|
|
+ public void acceptTermsOfService(boolean allowCrashUpload) {
|
|
|
+ assert mNativeSideIsInitializedSupplier.hasValue();
|
|
|
+
|
|
|
+- // If default is true then it corresponds to opt-out and false corresponds to opt-in.
|
|
|
+- UmaUtils.recordMetricsReportingDefaultOptIn(!DEFAULT_METRICS_AND_CRASH_REPORTING);
|
|
|
+- RecordHistogram.recordMediumTimesHistogram("MobileFre.FromLaunch.TosAccepted",
|
|
|
+- SystemClock.elapsedRealtime() - mIntentCreationElapsedRealtimeMs);
|
|
|
+ FirstRunUtils.acceptTermsOfService(allowCrashUpload);
|
|
|
+ FirstRunStatus.setSkipWelcomePage(true);
|
|
|
+ flushPersistentData();
|
|
|
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
|
|
|
+--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
|
|
|
++++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
|
|
|
+@@ -26,15 +26,8 @@ import org.chromium.chrome.browser.flags.ChromeSwitches;
|
|
|
+ import org.chromium.chrome.browser.locale.LocaleManager;
|
|
|
+ import org.chromium.chrome.browser.profiles.Profile;
|
|
|
+ import org.chromium.chrome.browser.search_engines.SearchEnginePromoType;
|
|
|
+-import org.chromium.chrome.browser.signin.services.FREMobileIdentityConsistencyFieldTrial;
|
|
|
+-import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
|
|
|
+-import org.chromium.chrome.browser.signin.services.SigninManager;
|
|
|
+ import org.chromium.chrome.browser.vr.VrModuleProvider;
|
|
|
+ import org.chromium.components.embedder_support.util.UrlConstants;
|
|
|
+-import org.chromium.components.signin.AccountManagerFacadeProvider;
|
|
|
+-import org.chromium.components.signin.AccountUtils;
|
|
|
+-import org.chromium.components.signin.identitymanager.ConsentLevel;
|
|
|
+-import org.chromium.components.signin.identitymanager.IdentityManager;
|
|
|
+
|
|
|
+ import java.util.List;
|
|
|
+
|
|
|
+@@ -59,27 +52,7 @@ public abstract class FirstRunFlowSequencer {
|
|
|
+ /** Returns true if the sync consent promo page should be shown. */
|
|
|
+ boolean shouldShowSyncConsentPage(
|
|
|
+ Activity activity, List<Account> accounts, boolean isChild) {
|
|
|
+- if (isChild) {
|
|
|
+- // Always show the sync consent page for child account.
|
|
|
+- return true;
|
|
|
+- }
|
|
|
+- final IdentityManager identityManager =
|
|
|
+- IdentityServicesProvider.get().getIdentityManager(
|
|
|
+- Profile.getLastUsedRegularProfile());
|
|
|
+- if (identityManager.hasPrimaryAccount(ConsentLevel.SYNC) || !isSyncAllowed()) {
|
|
|
+- // No need to show the sync consent page if users already consented to sync or
|
|
|
+- // if sync is not allowed.
|
|
|
+ return false;
|
|
|
+- }
|
|
|
+- if (FREMobileIdentityConsistencyFieldTrial.isEnabled()) {
|
|
|
+- // Show the sync consent page only to the signed-in users.
|
|
|
+- return identityManager.hasPrimaryAccount(ConsentLevel.SIGNIN);
|
|
|
+- } else {
|
|
|
+- // We show the sync consent page if sync is allowed, and not signed in, and
|
|
|
+- // - "skip the first use hints" is not set, or
|
|
|
+- // - "skip the first use hints" is set, but there is at least one account.
|
|
|
+- return !shouldSkipFirstUseHints(activity) || !accounts.isEmpty();
|
|
|
+- }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** @return true if the Search Engine promo page should be shown. */
|
|
|
+@@ -94,10 +67,7 @@ public abstract class FirstRunFlowSequencer {
|
|
|
+ /** @return true if Sync is allowed for the current user. */
|
|
|
+ @VisibleForTesting
|
|
|
+ protected boolean isSyncAllowed() {
|
|
|
+- SigninManager signinManager = IdentityServicesProvider.get().getSigninManager(
|
|
|
+- Profile.getLastUsedRegularProfile());
|
|
|
+- return FirstRunUtils.canAllowSync() && !signinManager.isSigninDisabledByPolicy()
|
|
|
+- && signinManager.isSigninSupported();
|
|
|
++ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** @return true if first use hints should be skipped. */
|
|
|
+@@ -141,19 +111,8 @@ public abstract class FirstRunFlowSequencer {
|
|
|
+ * Once finished, calls onFlowIsKnown().
|
|
|
+ */
|
|
|
+ void start() {
|
|
|
+- long childAccountStatusStart = SystemClock.elapsedRealtime();
|
|
|
+- AccountManagerFacadeProvider.getInstance().getAccounts().then(accounts -> {
|
|
|
+- AccountUtils.checkChildAccountStatus(
|
|
|
+- AccountManagerFacadeProvider.getInstance(), accounts, (isChild, account) -> {
|
|
|
+- RecordHistogram.recordCountHistogram(
|
|
|
+- "Signin.AndroidDeviceAccountsNumberWhenEnteringFRE",
|
|
|
+- Math.min(accounts.size(), 2));
|
|
|
+- RecordHistogram.recordTimesHistogram("MobileFre.ChildAccountStatusDuration",
|
|
|
+- SystemClock.elapsedRealtime() - childAccountStatusStart);
|
|
|
+- initializeSharedState(isChild, accounts);
|
|
|
+- processFreEnvironmentPreNative();
|
|
|
+- });
|
|
|
+- });
|
|
|
++ mIsChild = false;
|
|
|
++ processFreEnvironmentPreNative();
|
|
|
+ }
|
|
|
+
|
|
|
+ @VisibleForTesting
|
|
|
+@@ -162,22 +121,15 @@ public abstract class FirstRunFlowSequencer {
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean shouldShowSyncConsentPage() {
|
|
|
+- return mDelegate.shouldShowSyncConsentPage(mActivity, mGoogleAccounts, mIsChild);
|
|
|
++ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ @VisibleForTesting
|
|
|
+ protected void setFirstRunFlowSignInComplete() {
|
|
|
+- FirstRunSignInProcessor.setFirstRunFlowSignInComplete(true);
|
|
|
+- }
|
|
|
+-
|
|
|
+- private void initializeSharedState(boolean isChild, List<Account> accounts) {
|
|
|
+- mIsChild = isChild;
|
|
|
+- mGoogleAccounts = accounts;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void processFreEnvironmentPreNative() {
|
|
|
+ Bundle freProperties = new Bundle();
|
|
|
+- freProperties.putBoolean(SyncConsentFirstRunFragment.IS_CHILD_ACCOUNT, mIsChild);
|
|
|
+
|
|
|
+ onFlowIsKnown(freProperties);
|
|
|
+ if (mIsChild) {
|
|
|
+@@ -191,8 +143,8 @@ public abstract class FirstRunFlowSequencer {
|
|
|
+ * @param freProperties Resulting FRE properties bundle.
|
|
|
+ */
|
|
|
+ public void updateFirstRunProperties(Bundle freProperties) {
|
|
|
+- freProperties.putBoolean(
|
|
|
+- FirstRunActivity.SHOW_SYNC_CONSENT_PAGE, shouldShowSyncConsentPage());
|
|
|
++ if (freProperties == null)
|
|
|
++ throw new RuntimeException("attempting to update null FRE properties");
|
|
|
+ freProperties.putBoolean(
|
|
|
+ FirstRunActivity.SHOW_SEARCH_ENGINE_PAGE, shouldShowSearchEnginePage());
|
|
|
+ }
|
|
|
+@@ -202,17 +154,15 @@ public abstract class FirstRunFlowSequencer {
|
|
|
+ * @param syncConsentAccountName The account name for the pending sign-in request. (Or null)
|
|
|
+ * @param showAdvancedSyncSettings Whether the user selected to see the settings once signed in.
|
|
|
+ */
|
|
|
+- public static void markFlowAsCompleted(
|
|
|
+- String syncConsentAccountName, boolean showAdvancedSyncSettings) {
|
|
|
++ public static void markFlowAsCompleted() {
|
|
|
+ // When the user accepts ToS in the Setup Wizard, we do not show the ToS page to the user
|
|
|
+ // because the user has already accepted one outside FRE.
|
|
|
+ if (!FirstRunUtils.isFirstRunEulaAccepted()) {
|
|
|
+ FirstRunUtils.setEulaAccepted();
|
|
|
+ }
|
|
|
+
|
|
|
+- // Mark the FRE flow as complete and set the sign-in flow preferences if necessary.
|
|
|
+- FirstRunSignInProcessor.finalizeFirstRunFlowState(
|
|
|
+- syncConsentAccountName, showAdvancedSyncSettings);
|
|
|
++ // Mark the FRE flow as complete.
|
|
|
++ FirstRunStatus.setFirstRunFlowComplete(true);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+@@ -257,6 +207,7 @@ public abstract class FirstRunFlowSequencer {
|
|
|
+ || FirstRunStatus.getLightweightFirstRunFlowComplete())) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
++
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -304,15 +255,21 @@ public abstract class FirstRunFlowSequencer {
|
|
|
+ freIntent =
|
|
|
+ VrModuleProvider.getIntentDelegate().setupVrFreIntent(caller, freIntent);
|
|
|
+ // We cannot access Chrome right now, e.g. because the VR module is not installed.
|
|
|
+- if (freIntent == null) return true;
|
|
|
++ if (freIntent == null) {
|
|
|
++ throw new RuntimeException("Cannot access Bromite right now");
|
|
|
++ }
|
|
|
++ }
|
|
|
++ if (!IntentUtils.safeStartActivity(caller, freIntent)) {
|
|
|
++ throw new RuntimeException("Cannot start FirstRunExperience activity");
|
|
|
+ }
|
|
|
+- IntentUtils.safeStartActivity(caller, freIntent);
|
|
|
+ } else {
|
|
|
+ // First Run requires that the Intent contains NEW_TASK so that it doesn't sit on top
|
|
|
+ // of something else.
|
|
|
+ Intent newIntent = new Intent(fromIntent);
|
|
|
+ newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
|
+- IntentUtils.safeStartActivity(caller, newIntent);
|
|
|
++ if (!IntentUtils.safeStartActivity(caller, newIntent)) {
|
|
|
++ throw new RuntimeException("Cannot start FirstRunExperience activity");
|
|
|
++ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunUtils.java
|
|
|
+--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunUtils.java
|
|
|
++++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunUtils.java
|
|
|
+@@ -17,9 +17,6 @@ import org.chromium.chrome.browser.metrics.UmaSessionStats;
|
|
|
+ import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
|
|
|
+ import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
|
|
|
+ import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
|
|
|
+-import org.chromium.components.signin.AccountManagerFacade;
|
|
|
+-import org.chromium.components.signin.AccountManagerFacadeProvider;
|
|
|
+-import org.chromium.components.signin.AccountUtils;
|
|
|
+
|
|
|
+ /** Provides first run related utility functions. */
|
|
|
+ public class FirstRunUtils {
|
|
|
+@@ -80,24 +77,20 @@ public class FirstRunUtils {
|
|
|
+ * @return Whether or not sync is allowed on this device.
|
|
|
+ */
|
|
|
+ static boolean canAllowSync() {
|
|
|
+- return (hasGoogleAccountAuthenticator() && hasSyncPermissions()) || hasGoogleAccounts();
|
|
|
++ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ @VisibleForTesting
|
|
|
+ static boolean hasGoogleAccountAuthenticator() {
|
|
|
+ if (sHasGoogleAccountAuthenticator == null) {
|
|
|
+- AccountManagerFacade accountHelper = AccountManagerFacadeProvider.getInstance();
|
|
|
+- sHasGoogleAccountAuthenticator = accountHelper.hasGoogleAccountAuthenticator();
|
|
|
++ sHasGoogleAccountAuthenticator = false;
|
|
|
+ }
|
|
|
+ return sHasGoogleAccountAuthenticator;
|
|
|
+ }
|
|
|
+
|
|
|
+ @VisibleForTesting
|
|
|
+ static boolean hasGoogleAccounts() {
|
|
|
+- return !AccountUtils
|
|
|
+- .getAccountsIfFulfilledOrEmpty(
|
|
|
+- AccountManagerFacadeProvider.getInstance().getAccounts())
|
|
|
+- .isEmpty();
|
|
|
++ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressLint("InlinedApi")
|
|
|
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java
|
|
|
+--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java
|
|
|
++++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java
|
|
|
+@@ -16,16 +16,18 @@ import android.widget.Button;
|
|
|
+ import android.widget.CheckBox;
|
|
|
+ import android.widget.TextView;
|
|
|
+
|
|
|
++import android.content.SharedPreferences;
|
|
|
++import org.chromium.chrome.browser.omaha.OmahaBase;
|
|
|
++
|
|
|
+ import androidx.annotation.NonNull;
|
|
|
+ import androidx.annotation.Nullable;
|
|
|
+ import androidx.annotation.VisibleForTesting;
|
|
|
+ import androidx.fragment.app.Fragment;
|
|
|
+
|
|
|
++import org.chromium.base.Log;
|
|
|
+ import org.chromium.base.metrics.RecordHistogram;
|
|
|
+ import org.chromium.chrome.R;
|
|
|
+ import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManagerImpl;
|
|
|
+-import org.chromium.chrome.browser.signin.services.FREMobileIdentityConsistencyFieldTrial;
|
|
|
+-import org.chromium.chrome.browser.ui.signin.fre.FreUMADialogCoordinator;
|
|
|
+ import org.chromium.components.version_info.VersionInfo;
|
|
|
+ import org.chromium.ui.modaldialog.ModalDialogManagerHolder;
|
|
|
+ import org.chromium.ui.text.NoUnderlineClickableSpan;
|
|
|
+@@ -41,7 +43,7 @@ import java.util.List;
|
|
|
+ * User Metrics Analysis) as defined in the Chrome Privacy Notice.
|
|
|
+ */
|
|
|
+ public class ToSAndUMAFirstRunFragment
|
|
|
+- extends Fragment implements FirstRunFragment, FreUMADialogCoordinator.Listener {
|
|
|
++ extends Fragment implements FirstRunFragment {
|
|
|
+ /** Alerts about some methods once ToSAndUMAFirstRunFragment executes them. */
|
|
|
+ public interface Observer {
|
|
|
+ /** See {@link #onNativeInitialized}. */
|
|
|
+@@ -58,11 +60,10 @@ public class ToSAndUMAFirstRunFragment
|
|
|
+ private boolean mNativeInitialized;
|
|
|
+ private boolean mPolicyServiceInitialized;
|
|
|
+ private boolean mTosButtonClicked;
|
|
|
+- // TODO(https://crbug.com/1274145): Rename mAllowCrashUpload field.
|
|
|
+- private boolean mAllowCrashUpload;
|
|
|
+
|
|
|
+ private Button mAcceptButton;
|
|
|
+- private CheckBox mSendReportCheckBox;
|
|
|
++ private CheckBox mAutoUpdaterCheckBox;
|
|
|
++ private boolean mAutoUpdaterChecked;
|
|
|
+ private TextView mTosAndPrivacy;
|
|
|
+ private View mTitle;
|
|
|
+ private View mProgressSpinner;
|
|
|
+@@ -89,13 +90,15 @@ public class ToSAndUMAFirstRunFragment
|
|
|
+ mProgressSpinner = view.findViewById(R.id.progress_spinner);
|
|
|
+ mProgressSpinner.setVisibility(View.GONE);
|
|
|
+ mAcceptButton = (Button) view.findViewById(R.id.terms_accept);
|
|
|
+- mSendReportCheckBox = (CheckBox) view.findViewById(R.id.send_report_checkbox);
|
|
|
++ mAutoUpdaterCheckBox = (CheckBox) view.findViewById(R.id.auto_updater_checkbox);
|
|
|
+ mTosAndPrivacy = (TextView) view.findViewById(R.id.tos_and_privacy);
|
|
|
+
|
|
|
+ // Register event listeners.
|
|
|
+ mAcceptButton.setOnClickListener((v) -> onTosButtonClicked());
|
|
|
+- mSendReportCheckBox.setOnCheckedChangeListener(
|
|
|
+- ((compoundButton, isChecked) -> mAllowCrashUpload = isChecked));
|
|
|
++ mAutoUpdaterCheckBox.setOnCheckedChangeListener(
|
|
|
++ ((compoundButton, isChecked) -> {
|
|
|
++ mAutoUpdaterChecked = isChecked;
|
|
|
++ }));
|
|
|
+
|
|
|
+ // Make TextView links clickable.
|
|
|
+ mTosAndPrivacy.setMovementMethod(LinkMovementMethod.getInstance());
|
|
|
+@@ -111,7 +114,9 @@ public class ToSAndUMAFirstRunFragment
|
|
|
+ // initialized at which point the activity will skip the page.
|
|
|
+ // We distinguish case 1 from case 2 by the value of |mNativeInitialized|, as that is set
|
|
|
+ // via onAttachFragment() from FirstRunActivity - which is before this onViewCreated().
|
|
|
+- if (isWaitingForNativeAndPolicyInit() && FirstRunStatus.shouldSkipWelcomePage()) {
|
|
|
++ boolean isW = isWaitingForNativeAndPolicyInit();
|
|
|
++ boolean ssw = FirstRunStatus.shouldSkipWelcomePage();
|
|
|
++ if (isW && ssw) {
|
|
|
+ setSpinnerVisible(true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+@@ -137,7 +142,7 @@ public class ToSAndUMAFirstRunFragment
|
|
|
+ // On certain versions of Android, the checkbox will appear unchecked upon revisiting
|
|
|
+ // the page. Force it to the end state of the drawable animation as a work around.
|
|
|
+ // crbug.com/666258
|
|
|
+- mSendReportCheckBox.jumpDrawablesToCurrentState();
|
|
|
++ mAutoUpdaterCheckBox.jumpDrawablesToCurrentState();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -164,13 +169,8 @@ public class ToSAndUMAFirstRunFragment
|
|
|
+ assert !isWaitingForNativeAndPolicyInit();
|
|
|
+
|
|
|
+ setSpinnerVisible(false);
|
|
|
+- mSendReportCheckBox.setChecked(mAllowCrashUpload);
|
|
|
+- }
|
|
|
+-
|
|
|
+- /** Implements {@link FreUMADialogCoordinator.Listener} */
|
|
|
+- @Override
|
|
|
+- public void onAllowCrashUploadChecked(boolean allowCrashUpload) {
|
|
|
+- mAllowCrashUpload = allowCrashUpload;
|
|
|
++ // always checked, regardless of current preference state
|
|
|
++ mAutoUpdaterCheckBox.setChecked(true);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void updateView() {
|
|
|
+@@ -179,110 +179,39 @@ public class ToSAndUMAFirstRunFragment
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+- final boolean umaDialogMayBeShown =
|
|
|
+- FREMobileIdentityConsistencyFieldTrial.shouldShowOldFreWithUmaDialog();
|
|
|
+- final boolean hasChildAccount = getPageDelegate().getProperties().getBoolean(
|
|
|
+- SyncConsentFirstRunFragment.IS_CHILD_ACCOUNT, false);
|
|
|
+- final boolean isMetricsReportingDisabledByPolicy = !isWaitingForNativeAndPolicyInit()
|
|
|
+- && !PrivacyPreferencesManagerImpl.getInstance()
|
|
|
+- .isUsageAndCrashReportingPermittedByPolicy();
|
|
|
++ updateTosText();
|
|
|
+
|
|
|
+- updateTosText(umaDialogMayBeShown, hasChildAccount, isMetricsReportingDisabledByPolicy);
|
|
|
+-
|
|
|
+- updateReportCheckbox(umaDialogMayBeShown, isMetricsReportingDisabledByPolicy);
|
|
|
++ updateReportCheckbox();
|
|
|
+ }
|
|
|
+
|
|
|
+- private SpanInfo buildTermsOfServiceLink() {
|
|
|
+- NoUnderlineClickableSpan clickableGoogleTermsSpan =
|
|
|
++ private SpanInfo buildPrivacyPolicyLink(String suffix, int url) {
|
|
|
++ NoUnderlineClickableSpan clickableSpan =
|
|
|
+ new NoUnderlineClickableSpan(getContext(), (view1) -> {
|
|
|
+ if (!isAdded()) return;
|
|
|
+- getPageDelegate().showInfoPage(R.string.google_terms_of_service_url);
|
|
|
++ getPageDelegate().showInfoPage(url);
|
|
|
+ });
|
|
|
+- return new SpanInfo("<TOS_LINK>", "</TOS_LINK>", clickableGoogleTermsSpan);
|
|
|
+- }
|
|
|
+-
|
|
|
+- private SpanInfo buildAdditionalTermsOfServiceLink() {
|
|
|
+- NoUnderlineClickableSpan clickableChromeAdditionalTermsSpan =
|
|
|
+- new NoUnderlineClickableSpan(getContext(), (view1) -> {
|
|
|
+- if (!isAdded()) return;
|
|
|
+- getPageDelegate().showInfoPage(R.string.chrome_additional_terms_of_service_url);
|
|
|
+- });
|
|
|
+- return new SpanInfo("<ATOS_LINK>", "</ATOS_LINK>", clickableChromeAdditionalTermsSpan);
|
|
|
+- }
|
|
|
+-
|
|
|
+- private SpanInfo buildPrivacyPolicyLink() {
|
|
|
+- NoUnderlineClickableSpan clickableFamilyLinkPrivacySpan =
|
|
|
+- new NoUnderlineClickableSpan(getContext(), (view1) -> {
|
|
|
+- if (!isAdded()) return;
|
|
|
+- getPageDelegate().showInfoPage(R.string.google_privacy_policy_url);
|
|
|
+- });
|
|
|
+-
|
|
|
+- return new SpanInfo("<PRIVACY_LINK>", "</PRIVACY_LINK>", clickableFamilyLinkPrivacySpan);
|
|
|
+- }
|
|
|
+
|
|
|
+- private SpanInfo buildMetricsAndCrashReportingLink() {
|
|
|
+- NoUnderlineClickableSpan clickableUMADialogSpan =
|
|
|
+- new NoUnderlineClickableSpan(getContext(), (view1) -> openUmaDialog());
|
|
|
+- return new SpanInfo("<UMA_LINK>", "</UMA_LINK>", clickableUMADialogSpan);
|
|
|
++ return new SpanInfo("<PRIVACY_LINK" + suffix + ">", "</PRIVACY_LINK" + suffix + ">", clickableSpan);
|
|
|
+ }
|
|
|
+
|
|
|
+- private void updateTosText(boolean umaDialogMayBeShown, boolean hasChildAccount,
|
|
|
+- boolean isMetricsReportingDisabledByPolicy) {
|
|
|
++ private void updateTosText() {
|
|
|
+ List<SpanInfo> spans = new LinkedList<SpanInfo>();
|
|
|
+
|
|
|
+- // Description always has a Terms of Service link.
|
|
|
+- spans.add(buildTermsOfServiceLink());
|
|
|
++ spans.add(buildPrivacyPolicyLink("1", R.string.adblock_wiki_url));
|
|
|
+
|
|
|
+- // Additional terms of service link.
|
|
|
+- if (!umaDialogMayBeShown) {
|
|
|
+- spans.add(buildAdditionalTermsOfServiceLink());
|
|
|
+- }
|
|
|
++ spans.add(buildPrivacyPolicyLink("2", R.string.adblock_updater_privacy_policy_url));
|
|
|
+
|
|
|
+- // Privacy policy link.
|
|
|
+- if (hasChildAccount) {
|
|
|
+- spans.add(buildPrivacyPolicyLink());
|
|
|
+- }
|
|
|
++ spans.add(buildPrivacyPolicyLink("3", R.string.auto_updates_wiki_url));
|
|
|
+
|
|
|
+- // Metrics and crash reporting link.
|
|
|
+- if (umaDialogMayBeShown && !isMetricsReportingDisabledByPolicy) {
|
|
|
+- spans.add(buildMetricsAndCrashReportingLink());
|
|
|
+- }
|
|
|
++ spans.add(buildPrivacyPolicyLink("4", R.string.bromite_updater_privacy_policy_url));
|
|
|
+
|
|
|
+- String tosString;
|
|
|
+- if (umaDialogMayBeShown) {
|
|
|
+- tosString =
|
|
|
+- getString(hasChildAccount ? R.string.signin_fre_footer_tos_with_supervised_user
|
|
|
+- : R.string.signin_fre_footer_tos);
|
|
|
+-
|
|
|
+- if (!isMetricsReportingDisabledByPolicy) {
|
|
|
+- tosString += "\n" + getString(R.string.signin_fre_footer_metrics_reporting);
|
|
|
+- }
|
|
|
+- } else {
|
|
|
+- tosString = getString(hasChildAccount ? R.string.fre_tos_and_privacy_child_account
|
|
|
+- : R.string.fre_tos);
|
|
|
+- }
|
|
|
++ String tosString = getString(R.string.bromite_fre_footer_privacy_policy);
|
|
|
+
|
|
|
+ mTosAndPrivacy.setText(SpanApplier.applySpans(tosString, spans.toArray(new SpanInfo[0])));
|
|
|
+ }
|
|
|
+
|
|
|
+- private void updateReportCheckbox(
|
|
|
+- boolean umaDialogMayBeShown, boolean isMetricsReportingDisabledByPolicy) {
|
|
|
+- mAllowCrashUpload = getUmaCheckBoxInitialState();
|
|
|
+- mSendReportCheckBox.setChecked(mAllowCrashUpload);
|
|
|
+-
|
|
|
+- if (!canShowUmaCheckBox()) {
|
|
|
+- if (!umaDialogMayBeShown) {
|
|
|
+- mAllowCrashUpload = (sShowUmaCheckBoxForTesting || VersionInfo.isOfficialBuild())
|
|
|
+- && !isMetricsReportingDisabledByPolicy;
|
|
|
+- }
|
|
|
+- mSendReportCheckBox.setVisibility(View.GONE);
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- private void openUmaDialog() {
|
|
|
+- new FreUMADialogCoordinator(requireContext(),
|
|
|
+- ((ModalDialogManagerHolder) getActivity()).getModalDialogManager(), this,
|
|
|
+- mAllowCrashUpload);
|
|
|
++ private void updateReportCheckbox() {
|
|
|
++ mAutoUpdaterCheckBox.setChecked(true);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void onPolicyServiceInitialized(boolean onDevicePolicyFound) {
|
|
|
+@@ -309,6 +238,12 @@ public class ToSAndUMAFirstRunFragment
|
|
|
+ private void onTosButtonClicked() {
|
|
|
+ mTosButtonClicked = true;
|
|
|
+ mTosAcceptedTime = SystemClock.elapsedRealtime();
|
|
|
++
|
|
|
++ // save updater configuration only on button click
|
|
|
++ SharedPreferences.Editor sharedPreferenceEditor = OmahaBase.getSharedPreferences().edit();
|
|
|
++ sharedPreferenceEditor.putBoolean(OmahaBase.PREF_ALLOW_INLINE_UPDATE, mAutoUpdaterChecked);
|
|
|
++ sharedPreferenceEditor.apply();
|
|
|
++
|
|
|
+ tryMarkTermsAccepted(true);
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -319,7 +254,8 @@ public class ToSAndUMAFirstRunFragment
|
|
|
+ * @param fromButtonClicked Whether called from {@link #onTosButtonClicked()}.
|
|
|
+ */
|
|
|
+ private void tryMarkTermsAccepted(boolean fromButtonClicked) {
|
|
|
+- if (!mTosButtonClicked || isWaitingForNativeAndPolicyInit()) {
|
|
|
++ boolean isW = isWaitingForNativeAndPolicyInit();
|
|
|
++ if (!mTosButtonClicked || isW) {
|
|
|
+ if (fromButtonClicked) setSpinnerVisible(true);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+@@ -330,7 +266,7 @@ public class ToSAndUMAFirstRunFragment
|
|
|
+ RecordHistogram.recordTimesHistogram("MobileFre.TosFragment.SpinnerVisibleDuration",
|
|
|
+ SystemClock.elapsedRealtime() - mTosAcceptedTime);
|
|
|
+ }
|
|
|
+- getPageDelegate().acceptTermsOfService(mAllowCrashUpload);
|
|
|
++ getPageDelegate().acceptTermsOfService(false);
|
|
|
+ getPageDelegate().advanceToNextPage();
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -373,10 +309,7 @@ public class ToSAndUMAFirstRunFragment
|
|
|
+
|
|
|
+ mAcceptButton.setVisibility(visibility);
|
|
|
+ mTosAndPrivacy.setVisibility(visibility);
|
|
|
+- // Avoid updating visibility if the UMA check box can't be shown right now.
|
|
|
+- if (canShowUmaCheckBox()) {
|
|
|
+- mSendReportCheckBox.setVisibility(visibility);
|
|
|
+- }
|
|
|
++ mAutoUpdaterCheckBox.setVisibility(visibility);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected View getToSAndPrivacyText() {
|
|
|
+@@ -389,18 +322,6 @@ public class ToSAndUMAFirstRunFragment
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+- /**
|
|
|
+- * @return Whether the check box for Uma metrics can be shown. It should be used in conjunction
|
|
|
+- * with whether other non-spinner elements can generally be shown.
|
|
|
+- */
|
|
|
+- protected boolean canShowUmaCheckBox() {
|
|
|
+- return !FREMobileIdentityConsistencyFieldTrial.shouldShowOldFreWithUmaDialog()
|
|
|
+- && (sShowUmaCheckBoxForTesting || VersionInfo.isOfficialBuild())
|
|
|
+- && (isWaitingForNativeAndPolicyInit()
|
|
|
+- || PrivacyPreferencesManagerImpl.getInstance()
|
|
|
+- .isUsageAndCrashReportingPermittedByPolicy());
|
|
|
+- }
|
|
|
+-
|
|
|
+ @VisibleForTesting
|
|
|
+ public static void setShowUmaCheckBoxForTesting(boolean showForTesting) {
|
|
|
+ sShowUmaCheckBoxForTesting = showForTesting;
|
|
|
+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
|
|
|
+@@ -2817,11 +2817,30 @@ To change this setting, <ph name="BEGIN_LINK"><resetlink></ph>reset sync<p
|
|
|
+ <message name="IDS_LIGHTWEIGHT_FRE_ASSOCIATED_APP_TOS_AND_PRIVACY_CHILD_ACCOUNT" desc="Message explaining that use of Chrome is governed by Chrome's terms of service, and the Google Privacy Policy.">
|
|
|
+ <ph name="APP_NAME">%1$s<ex>Google Maps</ex></ph> will open in Chrome. By continuing, you agree to the <ph name="BEGIN_LINK1"><LINK1></ph>Google Terms of Service<ph name="END_LINK1"></LINK1></ph>, and the <ph name="BEGIN_LINK2"><LINK2></ph>Google Chrome and Chrome OS Additional Terms of Service<ph name="END_LINK2"></LINK2></ph>. The <ph name="BEGIN_LINK3"><LINK3></ph>Privacy Policy<ph name="END_LINK3"></LINK3></ph> also applies.
|
|
|
+ </message>
|
|
|
+- <message name="IDS_FRE_SEND_REPORT_CHECK" desc="Text for asking the user to allow sending stats and crash reports">
|
|
|
+- Help make Chrome better by sending usage statistics and crash reports to Google.
|
|
|
+- </message>
|
|
|
+ <message name="IDS_FRE_ACCEPT_CONTINUE" desc="Text for first page accept and continue button [CHAR_LIMIT=20]">
|
|
|
+- Accept & continue
|
|
|
++ Continue
|
|
|
++ </message>
|
|
|
++ <message name="IDS_ADBLOCK_UPDATER_PRIVACY_POLICY_URL" desc="URL for privacy policy for the ad block updater" translateable="false">
|
|
|
++ https://docs.github.com/en/github/site-policy/github-privacy-statement#github-pages
|
|
|
++ </message>
|
|
|
++ <message name="IDS_BROMITE_UPDATER_PRIVACY_POLICY_URL" desc="URL for privacy policy for the Bromite auto updater" translateable="false">
|
|
|
++ https://docs.github.com/en/github/site-policy/github-privacy-statement#github-pages
|
|
|
++ </message>
|
|
|
++ <message name="IDS_BROMITE_FRE_FOOTER_PRIVACY_POLICY" desc="Message explaining the privacy policy of the file hosting service provider for adblock updates and Bromite app automatic updates">
|
|
|
++ <ph name="BEGIN_PRIVACY_LINK1"><PRIVACY_LINK1></ph>Automatic ad block filters updates<ph name="END_PRIVACY_LINK1"></PRIVACY_LINK1></ph> are subject to the <ph name="BEGIN_PRIVACY_LINK2"><PRIVACY_LINK2></ph>GitHub Privacy statement<ph name="END_PRIVACY_LINK2"></PRIVACY_LINK2></ph>; they cannot be disabled.
|
|
|
++ The following checkbox controls instead <ph name="BEGIN_PRIVACY_LINK3"><PRIVACY_LINK3></ph>automatic app updates<ph name="END_PRIVACY_LINK3"></PRIVACY_LINK3></ph> which are also subject to the <ph name="BEGIN_PRIVACY_LINK4"><PRIVACY_LINK4></ph>GitHub Privacy statement<ph name="END_PRIVACY_LINK4"></PRIVACY_LINK4></ph>.
|
|
|
++ </message>
|
|
|
++ <message name="IDS_AUTO_UPDATER_CHECK" desc="Message for the checkbox for automatic Bromite updates">
|
|
|
++ Automatic checks for Bromite app updates
|
|
|
++ </message>
|
|
|
++ <message name="IDS_UPDATER_PRIVACY_POLICY_URL" desc="URL for GitHub privacy statement" translateable="false">
|
|
|
++ https://docs.github.com/en/github/site-policy/github-privacy-statement#github-pages
|
|
|
++ </message>
|
|
|
++ <message name="IDS_ADBLOCK_WIKI_URL" desc="URL for Bromite wiki page about ad blocking" translateable="false">
|
|
|
++ https://github.com/bromite/bromite/wiki/AdBlocking
|
|
|
++ </message>
|
|
|
++ <message name="IDS_AUTO_UPDATES_WIKI_URL" desc="URL for Bromite wiki page about automatic updates" translateable="false">
|
|
|
++ https://github.com/bromite/bromite/wiki/AutomaticUpdates
|
|
|
+ </message>
|
|
|
+ <message name="IDS_FRE_WELCOME" desc="Text for greeting the user on Chrome First Run">
|
|
|
+ Welcome to Chrome
|
|
|
+--
|
|
|
+2.25.1
|