blob: fe532cd4706216788a2bc042ca3e3063cd0e20eb [file] [log] [blame]
// Copyright 2016 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.signin;
import android.content.Context;
import android.content.Intent;
import android.provider.Settings;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.chromium.base.task.PostTask;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.preferences.PreferencesLauncher;
import org.chromium.chrome.browser.signin.AccountSigninActivity.AccessPoint;
import org.chromium.chrome.browser.sync.ui.SyncCustomizationFragment;
import org.chromium.chrome.browser.util.IntentUtils;
import org.chromium.components.sync.AndroidSyncSettings;
import org.chromium.components.sync.AndroidSyncSettings.AndroidSyncSettingsObserver;
import org.chromium.content_public.browser.UiThreadTaskTraits;
/**
* A View that shows the user the next step they must complete to start syncing their data (eg.
* Recent Tabs or Bookmarks).
* If inflated manually, {@link SyncPromoView#init(int)} must be called before
* attaching this View to a ViewGroup.
*/
public class SyncPromoView extends LinearLayout implements AndroidSyncSettingsObserver {
private @AccessPoint int mAccessPoint;
private boolean mInitialized;
private TextView mTitle;
private TextView mDescription;
private Button mPositiveButton;
/**
* A convenience method to inflate and initialize a SyncPromoView.
* @param parent A parent used to provide LayoutParams (the SyncPromoView will not be
* attached).
* @param accessPoint Where the SyncPromoView is used.
*/
public static SyncPromoView create(ViewGroup parent, @AccessPoint int accessPoint) {
// TODO(injae): crbug.com/829548
SyncPromoView result = (SyncPromoView) LayoutInflater.from(parent.getContext())
.inflate(R.layout.sync_promo_view, parent, false);
result.init(accessPoint);
return result;
}
/**
* Constructor for inflating from xml.
*/
public SyncPromoView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mTitle = findViewById(R.id.title);
mDescription = findViewById(R.id.description);
mPositiveButton = findViewById(R.id.sign_in);
}
/**
* Provide the information necessary for this class to function.
* @param accessPoint Where this UI component is used.
*/
public void init(@AccessPoint int accessPoint) {
mAccessPoint = accessPoint;
mInitialized = true;
assert mAccessPoint == SigninAccessPoint.BOOKMARK_MANAGER
|| mAccessPoint == SigninAccessPoint.RECENT_TABS
: "SyncPromoView only has strings for bookmark manager and recent tabs.";
// The title stays the same no matter what action the user must take.
if (mAccessPoint == SigninAccessPoint.BOOKMARK_MANAGER) {
mTitle.setText(R.string.sync_your_bookmarks);
} else {
mTitle.setVisibility(View.GONE);
}
// We don't call update() here as it will be called in onAttachedToWindow().
}
private void update() {
ViewState viewState;
if (!AndroidSyncSettings.get().isMasterSyncEnabled()) {
viewState = getStateForEnableAndroidSync();
} else if (!AndroidSyncSettings.get().isChromeSyncEnabled()) {
viewState = getStateForEnableChromeSync();
} else {
viewState = getStateForStartUsing();
}
viewState.apply(mDescription, mPositiveButton);
}
/**
* The ViewState class represents all the UI elements that can change for each variation of
* this View. We use this to ensure each variation (created in the getStateFor* methods)
* explicitly touches each UI element.
*/
private static class ViewState {
private final int mDescriptionText;
private final ButtonState mPositiveButtonState;
public ViewState(int mDescriptionText, ButtonState mPositiveButtonState) {
this.mDescriptionText = mDescriptionText;
this.mPositiveButtonState = mPositiveButtonState;
}
public void apply(TextView description, Button positiveButton) {
description.setText(mDescriptionText);
mPositiveButtonState.apply(positiveButton);
}
}
/**
* Classes to represent the state of a button that we are interested in, used to keep ViewState
* tidy and provide some convenience methods.
*/
private interface ButtonState {
void apply(Button button);
}
private static class ButtonAbsent implements ButtonState {
@Override
public void apply(Button button) {
button.setVisibility(View.GONE);
}
}
private static class ButtonPresent implements ButtonState {
private final int mTextResource;
private final OnClickListener mOnClickListener;
public ButtonPresent(int textResource, OnClickListener onClickListener) {
mTextResource = textResource;
mOnClickListener = onClickListener;
}
@Override
public void apply(Button button) {
button.setVisibility(View.VISIBLE);
button.setText(mTextResource);
button.setOnClickListener(mOnClickListener);
}
}
private ViewState getStateForEnableAndroidSync() {
assert mAccessPoint == SigninAccessPoint.RECENT_TABS
: "Enable Android Sync should not be showing from bookmarks";
int descId = R.string.recent_tabs_sync_promo_enable_android_sync;
ButtonState positiveButton = new ButtonPresent(R.string.open_settings_button, view -> {
IntentUtils.safeStartActivity(getContext(), new Intent(Settings.ACTION_SYNC_SETTINGS));
});
return new ViewState(descId, positiveButton);
}
private ViewState getStateForEnableChromeSync() {
int descId = mAccessPoint == SigninAccessPoint.BOOKMARK_MANAGER
? R.string.bookmarks_sync_promo_enable_sync
: R.string.recent_tabs_sync_promo_enable_chrome_sync;
ButtonState positiveButton = new ButtonPresent(R.string.enable_sync_button,
view -> PreferencesLauncher.launchSettingsPage(
getContext(), SyncCustomizationFragment.class));
return new ViewState(descId, positiveButton);
}
private ViewState getStateForStartUsing() {
// TODO(peconn): Ensure this state is never seen when used for bookmarks.
// State is updated before this view is removed, so this invalid state happens, but is not
// visible. I want there to be a guarantee that this state is never seen, but to do so would
// require some code restructuring.
return new ViewState(R.string.ntp_recent_tabs_sync_promo_instructions, new ButtonAbsent());
}
@Override
protected void onAttachedToWindow() {
assert mInitialized : "init(...) must be called on SyncPromoView before use.";
super.onAttachedToWindow();
AndroidSyncSettings.get().registerObserver(this);
update();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
AndroidSyncSettings.get().unregisterObserver(this);
}
// AndroidSyncStateObserver
@Override
public void androidSyncSettingsChanged() {
// AndroidSyncSettings calls this method from non-UI threads.
PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, this::update);
}
}