Commit 60f51ca8 authored by aurimas's avatar aurimas Committed by Commit bot

Upstream Account chooser fragment of First Run.

BUG=428842
TBR=jbudorick@chromium.org

Review URL: https://codereview.chromium.org/954933004

Cr-Commit-Position: refs/heads/master@{#318280}
parent 3928ba71
......@@ -6,3 +6,4 @@ M D UuF: Unused public or protected field: org.chromium.chrome.browser.document.
M D UuF: Unused public or protected field: org.chromium.chrome.browser.document.PendingDocumentData.originalIntent In PendingDocumentData.java
M D UuF: Unused public or protected field: org.chromium.chrome.browser.document.PendingDocumentData.url In PendingDocumentData.java
M D UuF: Unused public or protected field: org.chromium.chrome.browser.document.PendingDocumentData.requestId In PendingDocumentData.java
M D BC: Unchecked/unconfirmed cast from android.view.View to org.chromium.chrome.browser.firstrun.AccountFirstRunView in org.chromium.chrome.browser.firstrun.AccountFirstRunFragment.onViewCreated(View, Bundle) At AccountFirstRunFragment.java
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2015 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.chrome.browser.firstrun.AccountFirstRunView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fre_account_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="57dp"
android:fillViewport="true"
android:scrollbarStyle="outsideOverlay" >
<LinearLayout
android:id="@+id/fre_account_linear_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical" >
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/fre_margin"
android:gravity="center"
android:lineSpacingMultiplier="1.4"
android:text="@string/fre_set_up_chrome"
android:textColor="@color/fre_title_color"
android:textSize="@dimen/fre_title_text_size" />
<LinearLayout
android:id="@+id/fre_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/fre_margin"
android:gravity="center_horizontal"
android:orientation="vertical" >
<org.chromium.chrome.browser.firstrun.ImageCarousel
android:id="@+id/image_slider"
android:layout_width="@dimen/fre_image_carousel_width"
android:layout_height="@dimen/fre_image_carousel_height"
android:layout_marginBottom="@dimen/fre_margin"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/fre_margin"
android:orientation="vertical"
android:gravity="center_horizontal"
android:paddingEnd="24dp"
android:paddingStart="24dp" >
<Spinner
android:id="@+id/google_accounts_spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/fre_margin"
android:padding="0dp"
android:contentDescription="@string/accessibility_fre_account_spinner"
android:textColor="@color/fre_text_color" />
<TextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:lineSpacingMultiplier="1.4"
android:text="@string/fre_account_choice_description"
android:textColor="@color/fre_light_text_color"
android:textSize="@dimen/fre_normal_text_size" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
<View android:id="@+id/button_bar_separator"
style="@style/ButtonBarTopDivider"
android:layout_gravity="bottom"
android:layout_marginBottom="56dp" />
<LinearLayout
android:id="@+id/button_bar"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_gravity="bottom"
android:orientation="horizontal" >
<Button
android:id="@+id/negative_button"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?attr/listChoiceBackgroundIndicator"
android:gravity="start|center_vertical"
android:textDirection="locale"
android:padding="16dp"
android:text="@string/fre_skip_text"
android:textAllCaps="true"
android:textColor="@color/light_normal_color"
android:textSize="@dimen/fre_button_text_size" />
<Button
android:id="@+id/positive_button"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?attr/listChoiceBackgroundIndicator"
android:gravity="end|center_vertical"
android:textDirection="locale"
android:padding="16dp"
android:text="@string/choose_account_sign_in"
android:textAllCaps="true"
android:textColor="@color/light_active_color"
android:textSize="@dimen/fre_button_text_size" />
</LinearLayout>
</org.chromium.chrome.browser.firstrun.AccountFirstRunView>
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2015 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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
style="?android:attr/spinnerDropDownItemStyle"
android:singleLine="true"
android:ellipsize="end"
android:maxWidth="216dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/fre_text_color"
android:textSize="@dimen/fre_normal_text_size"
android:padding="@dimen/fre_button_padding" />
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2015 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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fre_spinner_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxWidth="216dp"
android:layoutDirection="locale"
android:padding="@dimen/fre_button_padding"
android:paddingBottom="16dp"
android:paddingEnd="12dp"
android:paddingStart="12dp"
android:paddingTop="16dp"
android:textAlignment="viewStart"
android:ellipsize="end"
android:singleLine="true"
android:textColor="@color/fre_text_color"
android:textSize="@dimen/fre_normal_text_size" />
......@@ -68,4 +68,14 @@
<color name="compositor_tab_title_bar_shadow">#AAFFFFFF</color>
<color name="compositor_tab_title_bar_shadow_incognito">#88000000</color>
<!-- First Run Experience Colors -->
<color name="fre_text_color">#000000</color>
<color name="fre_light_text_color">#494949</color>
<color name="fre_title_color">#161616</color>
<!-- NTP Colors. Used on the bookmarks and recent tabs pages. -->
<color name="ntp_bg">#fff</color>
<!-- Signin promo screen colors -->
<color name="illustration_background_color">#4FC3F7</color>
</resources>
......@@ -105,4 +105,16 @@
<!-- Account Chooser infobar Dimensions -->
<dimen name="account_chooser_infobar_item_height">80dp</dimen>
<!-- First Run Experience and Welcome Page dimensions -->
<dimen name="fre_margin">24dp</dimen>
<dimen name="fre_title_text_size">24sp</dimen>
<dimen name="fre_button_text_size">14sp</dimen>
<dimen name="fre_button_padding">12dp</dimen>
<dimen name="fre_normal_text_size">14sp</dimen>
<dimen name="fre_image_carousel_width">260dp</dimen>
<dimen name="fre_image_carousel_height">130dp</dimen>
<dimen name="fre_checkmark_size">38dp</dimen>
<!-- Sign-in promo dimensions -->
<dimen name="sign_in_promo_padding_bottom">16dp</dimen>
</resources>
// Copyright 2015 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.app.Fragment;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.chromium.chrome.R;
/**
* A {@link Fragment} meant to handle sync setup for the first run experience.
*/
public class AccountFirstRunFragment extends FirstRunPage {
// Per-page parameters:
public static final String FORCE_SIGNIN_ACCOUNT_TO = "ForceSigninAccountTo";
public static final String PRESELECT_BUT_ALLOW_TO_CHANGE = "PreselectButAllowToChange";
public static final String FORCE_SIGNIN_AND_DISABLE_NO_THANKS = "ForceSigninAndDisableNoThanks";
public static final String IS_CHILD_ACCOUNT = "IsChildAccount";
private AccountFirstRunView mView;
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mView = (AccountFirstRunView) inflater.inflate(
R.layout.fre_choose_account, container, false);
return mView;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
AccountFirstRunView firstRunView = (AccountFirstRunView) view;
firstRunView.setListener(new AccountFirstRunView.Listener() {
@Override
public void onAccountSelectionConfirmed(String accountName) {
mView.switchToSignedMode();
}
@Override
public void onAccountSelectionCanceled() {
getPageDelegate().refuseSignIn();
advanceToNextPage();
}
@Override
public void onNewAccount() {
getPageDelegate().openAccountAdder(AccountFirstRunFragment.this);
}
@Override
public void onSigningInCompleted(String accountName) {
getPageDelegate().acceptSignIn(accountName);
advanceToNextPage();
}
@Override
public void onSettingsButtonClicked(String accountName) {
getPageDelegate().acceptSignIn(accountName);
getPageDelegate().askToOpenSyncSettings();
advanceToNextPage();
}
@Override
public void onFailedToSetForcedAccount(String forcedAccountName) {
// Somehow the forced account disappeared while we were in the FRE.
// The user would have to go through the FRE again.
getPageDelegate().abortFirstRunExperience();
}
});
firstRunView.init(getPageDelegate().getProfileDataCache());
if (getProperties().getBoolean(FORCE_SIGNIN_AND_DISABLE_NO_THANKS)) {
firstRunView.setCanCancel(false);
}
firstRunView.setIsChildAccount(getProperties().getBoolean(IS_CHILD_ACCOUNT));
String forcedAccountName =
getProperties().getString(FORCE_SIGNIN_ACCOUNT_TO);
if (!TextUtils.isEmpty(forcedAccountName)) {
firstRunView.switchToForcedAccountMode(forcedAccountName);
}
}
@Override
public void onStart() {
super.onStart();
((AccountFirstRunView) getView()).setButtonsEnabled(true);
((AccountFirstRunView) getView()).setProfileDataCache(
getPageDelegate().getProfileDataCache());
getPageDelegate().onSigninDialogShown();
}
// FirstRunPage:
@Override
public boolean interceptBackPressed() {
if (!mView.isSignedIn()
|| (mView.isInForcedAccountMode()
&& !getProperties().getBoolean(PRESELECT_BUT_ALLOW_TO_CHANGE))) {
return super.interceptBackPressed();
}
if (mView.isInForcedAccountMode()
&& getProperties().getBoolean(PRESELECT_BUT_ALLOW_TO_CHANGE)) {
// Allow the user to choose the account or refuse to sign in,
// and re-create this fragment.
getProperties().remove(FORCE_SIGNIN_ACCOUNT_TO);
}
// Re-create the fragment if the user presses the back button when in signed in mode.
// The fragment is re-created in the normal (signed out) mode.
getPageDelegate().recreateCurrentPage();
return true;
}
}
// Copyright 2015 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.app.Fragment;
import android.content.Context;
import android.os.Bundle;
/**
* A first run page shown in the First Run ViewPager.
*/
public class FirstRunPage extends Fragment {
private Bundle mProperties = new Bundle();
// FRE properties:
private static final String LAST_PAGE_NUMBER = "LastPageNumber";
// Per-page properties:
private static final String THIS_PAGE_NUMBER = "ThisPageNumber";
/**
* Appends page-specific properties to the page instance bundle.
* @param props Instance properties.
* @param pageNumber This page number in the First Run sequence.
* @param lastPageNumber Last page number in the First Run sequence.
* @return Instance properties with the page-specific properties added.
*/
public static Bundle addProperties(Bundle props, int pageNumber, int lastPageNumber) {
props.putInt(THIS_PAGE_NUMBER, pageNumber);
return props;
}
/**
* @return Whether this page should be skipped on the FRE resume.
* @param appContext An application context.
*/
public boolean shouldSkipPageOnResume(Context appContext) {
return false;
}
// Fragment:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mProperties = savedInstanceState;
} else if (getArguments() != null) {
mProperties = getArguments();
} else {
mProperties = new Bundle();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putAll(mProperties);
}
/**
* @return Whether the back button press was handled by this page.
*/
public boolean interceptBackPressed() {
return false;
}
/**
* @return Passed arguments if any, or saved instance state if any, or an empty bundle.
*/
protected Bundle getProperties() {
return mProperties;
}
/**
* @return The interface to the host.
*/
protected FirstRunPageDelegate getPageDelegate() {
return (FirstRunPageDelegate) getActivity();
}
/**
* @return Whether this page is the last page of the FRE.
*/
protected boolean isLastPage() {
return getProperties().getInt(THIS_PAGE_NUMBER) == getProperties().getInt(LAST_PAGE_NUMBER);
}
/**
* Advances to the next FRE page.
*/
protected void advanceToNextPage() {
getPageDelegate().advanceToNextPage();
}
}
\ No newline at end of file
// Copyright 2015 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.app.Fragment;
/**
* Defines the host interface for First Run Experience pages.
*/
public interface FirstRunPageDelegate {
/**
* @return A {@link ProfileDataCache} for Android user accounts.
*/
ProfileDataCache getProfileDataCache();
/**
* Advances the First Run Experience to the next page.
* Successfully finishes FRE if the current page is the last page.
*/
void advanceToNextPage();
/**
* Skips all intro pages.
* Successfully finishes FRE if there are no non-intro pages.
*/
void skipIntroPages();
/**
* Asks to re-instantiate the current page.
* Useful to restore the "clean" state of the UI elements.
*/
void recreateCurrentPage();
/**
* Unsuccessfully aborts the First Run Experience.
* This usually means that the application will be closed.
*/
void abortFirstRunExperience();
/**
* Successfully completes the First Run Experience.
* All results will be packaged and sent over to the main activity.
*/
void completeFirstRunExperience();
/**
* Notifies that the sign-in dialog is shown.
*/
void onSigninDialogShown();
/**
* Notifies that the user refused to sign in (e.g. "NO, THANKS").
*/
void refuseSignIn();
/**
* Notifies that the user accepted to be signed in.
* @param accountName An account to be signed in to.
*/
void acceptSignIn(String accountName);
/**
* Notifies that the user asked to show Sync Settings once the sign in
* process is complete.
*/
void askToOpenSyncSettings();
/**
* @return Whether the user has accepted Chrome Terms of Service.
*/
boolean didAcceptTermsOfService();
/**
* @return Whether the "upload crash dump" setting is set to "NEVER".
*/
boolean isNeverUploadCrashDump();
/**
* Notifies all interested parties that the user has accepted Chrome Terms of Service.
* @param allowCrashUpload True if the user allows to upload crash dumps and collect stats.
*/
void acceptTermsOfService(boolean allowCrashUpload);
/**
* Opens the Android account adder UI.
* @param fragment A fragment that needs the account adder UI.
*/
void openAccountAdder(Fragment fragment);
/**
* Opens Chrome Preferences on a given page.
* @param fragment A fragment that requested the service.
* @param pageClassName The preferences fragment class name.
*/
void openChromePreferencesPage(Fragment fragment, String pageClassName);
}
// Copyright 2015 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.accounts.Account;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.profiles.ProfileDownloader;
import org.chromium.chrome.browser.profiles.ProfileDownloader.Observer;
import org.chromium.sync.signin.AccountManagerHelper;
import org.chromium.ui.gfx.DeviceDisplayInfo;
import java.util.HashMap;
/**
* Fetches and caches Google Account profile images and full names for the accounts on the device.
*/
public class ProfileDataCache implements Observer {
private static final int PROFILE_IMAGE_SIZE_DP = 136; // Max size of the user picture.
private static final int PROFILE_IMAGE_STROKE_DP = 3;
private static class CacheEntry {
public CacheEntry(Bitmap picture, String fullName, String givenName) {
this.picture = picture;
this.fullName = fullName;
this.givenName = givenName;
}
public Bitmap picture;
public String fullName;
public String givenName;
}
private final HashMap<String, CacheEntry> mCacheEntries = new HashMap<String, CacheEntry>();
private final Bitmap mPlaceholderImage;
private final int mImageSizePx;
private final int mImageStrokePx;
private final int mImageStrokeColor;
private Observer mObserver;
private final Activity mActivity;
private Profile mProfile;
public ProfileDataCache(Activity activity, Profile profile) {
ProfileDownloader.addObserver(this);
mActivity = activity;
mProfile = profile;
final DeviceDisplayInfo info = DeviceDisplayInfo.create(activity);
mImageSizePx = (int) Math.ceil(PROFILE_IMAGE_SIZE_DP * info.getDIPScale());
mImageStrokePx = (int) Math.ceil(PROFILE_IMAGE_STROKE_DP * info.getDIPScale());
mImageStrokeColor = Color.WHITE;
Bitmap placeHolder = BitmapFactory.decodeResource(mActivity.getResources(),
R.drawable.fre_placeholder);
mPlaceholderImage = getCroppedBitmap(placeHolder);
update();
}
/**
* Sets the profile to use for the fetcher and triggers the update.
* @param profile A profile to use.
*/
public void setProfile(Profile profile) {
mProfile = profile;
update();
}
/**
* Initiate fetching the user accounts data (images and the full name).
* Fetched data will be sent to observers of ProfileDownloader.
*/
public void update() {
if (mProfile == null) return;
Account[] accounts = AccountManagerHelper.get(mActivity).getGoogleAccounts();
for (int i = 0; i < accounts.length; i++) {
if (mCacheEntries.get(accounts[i].name) == null) {
ProfileDownloader.startFetchingAccountInfoFor(
mProfile, accounts[i].name, mImageSizePx);
}
}
}
/**
* @param accountId Google account ID for the image that is requested.
* @return Returns the profile image for a given Google account ID if it's in
* the cache, otherwise returns a placeholder image.
*/
public Bitmap getImage(String accountId) {
CacheEntry cacheEntry = mCacheEntries.get(accountId);
if (cacheEntry == null) return mPlaceholderImage;
return cacheEntry.picture;
}
/**
* @param accountId Google account ID for the full name that is requested.
* @return Returns the full name for a given Google account ID if it is
* the cache, otherwise returns null.
*/
public String getFullName(String accountId) {
CacheEntry cacheEntry = mCacheEntries.get(accountId);
if (cacheEntry == null) return null;
return cacheEntry.fullName;
}
/**
* @param accountId Google account ID for the full name that is requested.
* @return Returns the given name for a given Google account ID if it is in the cache, otherwise
* returns null.
*/
public String getGivenName(String accountId) {
CacheEntry cacheEntry = mCacheEntries.get(accountId);
if (cacheEntry == null) return null;
return cacheEntry.givenName;
}
public void onDestroy() {
ProfileDownloader.removeObserver(this);
mObserver = null;
}
@Override
public void onProfileDownloaded(String accountId, String fullName, String givenName,
Bitmap bitmap) {
bitmap = getCroppedBitmap(bitmap);
mCacheEntries.put(accountId, new CacheEntry(bitmap, fullName, givenName));
if (mObserver != null) mObserver.onProfileDownloaded(accountId, givenName, fullName,
bitmap);
}
private Bitmap getCroppedBitmap(Bitmap bitmap) {
Bitmap output = Bitmap.createBitmap(
bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(Color.WHITE);
final float radius = (bitmap.getWidth() - mImageStrokePx) / 2f;
canvas.drawCircle(bitmap.getWidth() / 2f, bitmap.getHeight() / 2f, radius, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
paint.setColor(mImageStrokeColor);
paint.setStyle(Paint.Style.STROKE);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC));
paint.setStrokeWidth(mImageStrokePx);
canvas.drawCircle(bitmap.getWidth() / 2f, bitmap.getHeight() / 2f, radius, paint);
return output;
}
/**
* @param observer Observer that should be notified when new profile images are available.
*/
public void setObserver(Observer observer) {
mObserver = observer;
}
}
......@@ -1241,6 +1241,53 @@ Drag from top to exit.
Don't ask again for this card
</message>
<!-- First Run strings -->
<message name="IDS_FRE_SET_UP_CHROME" desc="Header text shown when the user is first setting up Chrome">
Set up Chrome
</message>
<message name="IDS_FRE_ACCOUNT_CHOICE_DESCRIPTION" desc="Text for account chooser page">
Select an account to get your tabs, bookmarks, history, and other settings on all your devices.
</message>
<message name="IDS_FRE_SKIP_TEXT" desc="Text for second page skip button">
No thanks
</message>
<message name="IDS_CHOOSE_ACCOUNT_SIGN_IN" desc="Sign in button for choose google account dialog [CHAR-LIMIT=20]">
Sign in
</message>
<message name="IDS_FRE_DONE" desc="Button to dismiss the sign-in screen once the user is done">
Done
</message>
<message name="IDS_FRE_SETTINGS" desc="Button that opens Chrome settings">
Settings
</message>
<message name="IDS_FRE_ADD_ACCOUNT" desc="Text for adding another Google Account">
Add account
</message>
<message name="IDS_MAKE_CHROME_YOURS" desc="Title for the signin promo that prompts the user to sign in to Chrome to have a personalized experirence. [CHAR-LIMIT=24]">
Make Chrome yours
</message>
<message name="IDS_FRE_NO_ACCOUNT_CHOICE_DESCRIPTION" desc="Text for account chooser page when there are no accounts on the device.">
Add an account to get your tabs, bookmarks, history, and other settings on all your devices.
</message>
<message name="IDS_SIGN_IN_TO_CHROME_SUMMARY_VARIANT" desc="A variant of summary for the signin promo; lists reasons to sign in to Chrome.">
Sign in to access all your web stuff from any device
</message>
<message name="IDS_FRE_NO_ACCOUNTS" desc="Text for spinner when there is no google accounts [CHAR-LIMIT=40]">
Add an account
</message>
<message name="IDS_FRE_HI_NAME" desc="Title welcoming the user by their name after they have signed in.">
Hi, <ph name="FULL_NAME">%1$s<ex>John Smith</ex></ph>
</message>
<message name="IDS_FRE_SIGNED_IN_DESCRIPTION" desc="Explanation of what gets synced after the user signed in">
Your tabs, bookmarks, history and other settings will be synced to your Google Account.
</message>
<message name="IDS_FRE_SIGNED_IN_DESCRIPTION_UCA_ADDENDUM" desc="Additional explanation for child accounts that synced settings are managed by parents.">
Your parents help manage these settings.
</message>
<message name="IDS_ACCESSIBILITY_FRE_ACCOUNT_SPINNER" desc="Content description for the first run account drop down spinner.">
Choose account
</message>
</messages>
</release>
</grit>
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