chromium/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncPromoPreference.java

// Copyright 2020 The Chromium Authors
// 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.sync.settings;

import android.content.Context;
import android.util.AttributeSet;

import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;

import org.chromium.chrome.R;
import org.chromium.chrome.browser.signin.services.ProfileDataCache;
import org.chromium.chrome.browser.signin.services.SigninManager;
import org.chromium.chrome.browser.signin.services.SigninManager.SignInStateObserver;
import org.chromium.chrome.browser.ui.signin.PersonalizedSigninPromoView;
import org.chromium.chrome.browser.ui.signin.SyncPromoController;
import org.chromium.components.signin.AccountManagerFacade;
import org.chromium.components.signin.AccountsChangeObserver;
import org.chromium.components.signin.SigninFeatureMap;
import org.chromium.components.signin.SigninFeatures;
import org.chromium.components.signin.identitymanager.ConsentLevel;
import org.chromium.components.signin.identitymanager.IdentityManager;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/** A preference that displays Personalized Sync Promo when the user is not syncing. */
public class SyncPromoPreference extends Preference
        implements SignInStateObserver, ProfileDataCache.Observer, AccountsChangeObserver {
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({State.PROMO_HIDDEN, State.PERSONALIZED_SIGNIN_PROMO, State.PERSONALIZED_SYNC_PROMO})
    public @interface State {
        int PROMO_HIDDEN = 0;
        int PERSONALIZED_SIGNIN_PROMO = 1;
        int PERSONALIZED_SYNC_PROMO = 2;
    }

    private ProfileDataCache mProfileDataCache;
    private AccountManagerFacade mAccountManagerFacade;
    private SigninManager mSigninManager;
    private IdentityManager mIdentityManager;

    private @State int mState;
    private @Nullable SyncPromoController mSyncPromoController;

    /** Constructor for inflating from XML. */
    public SyncPromoPreference(Context context, AttributeSet attrs) {
        super(context, attrs);
        setLayoutResource(R.layout.sync_promo_view_settings);

        // State will be updated in onAttached.
        mState = State.PROMO_HIDDEN;
        setVisible(false);
    }

    /**
     * Initialize the dependencies for the SyncPromoPreference.
     *
     * <p>Must be called before the preference is attached, which is called from the containing
     * settings screen's onViewCreated method.
     */
    public void initialize(
            ProfileDataCache profileDataCache,
            AccountManagerFacade accountManagerFacade,
            SigninManager signinManager,
            IdentityManager identityManager,
            SyncPromoController syncPromoController) {
        mProfileDataCache = profileDataCache;
        mAccountManagerFacade = accountManagerFacade;
        mSigninManager = signinManager;
        mIdentityManager = identityManager;
        mSyncPromoController = syncPromoController;
    }

    @Override
    public void onAttached() {
        super.onAttached();

        mAccountManagerFacade.addObserver(this);
        mSigninManager.addSignInStateObserver(this);
        mProfileDataCache.addObserver(this);

        update();
    }

    @Override
    public void onDetached() {
        super.onDetached();

        mAccountManagerFacade.removeObserver(this);
        mSigninManager.removeSignInStateObserver(this);
        mProfileDataCache.removeObserver(this);
        mSyncPromoController = null;
    }

    /** Returns the state of the preference. Not valid until registerForUpdates is called. */
    public @State int getState() {
        return mState;
    }

    private void setState(@State int state) {
        if (mState == state) return;

        final boolean hasStateChangedFromHiddenToShown =
                mState == State.PROMO_HIDDEN
                        && (state == State.PERSONALIZED_SIGNIN_PROMO
                                || state == State.PERSONALIZED_SYNC_PROMO);
        if (hasStateChangedFromHiddenToShown) {
            mSyncPromoController.increasePromoShowCount();
        }

        mState = state;
    }

    /** Updates the title, summary, and image based on the current sign-in state. */
    private void update() {
        if (SigninFeatureMap.isEnabled(SigninFeatures.HIDE_SETTINGS_SIGN_IN_PROMO)
                || mSigninManager.isSigninDisabledByPolicy()) {
            setupPromoHidden();
            return;
        }

        if (mSyncPromoController.canShowSyncPromo()) {
            if (!mIdentityManager.hasPrimaryAccount(ConsentLevel.SIGNIN)) {
                setupPersonalizedPromo(State.PERSONALIZED_SIGNIN_PROMO);
                return;
            }

            if (!mIdentityManager.hasPrimaryAccount(ConsentLevel.SYNC)) {
                setupPersonalizedPromo(State.PERSONALIZED_SYNC_PROMO);
                return;
            }
        }

        setupPromoHidden();
    }

    private void setupPersonalizedPromo(@State int state) {
        setState(state);
        setSelectable(false);
        setVisible(true);
        notifyChanged();
    }

    private void setupPromoHidden() {
        setState(State.PROMO_HIDDEN);
        setVisible(false);
    }

    @Override
    public void onBindViewHolder(PreferenceViewHolder holder) {
        super.onBindViewHolder(holder);

        if (mState == State.PROMO_HIDDEN) return;

        PersonalizedSigninPromoView syncPromoView =
                (PersonalizedSigninPromoView) holder.findViewById(R.id.signin_promo_view_container);
        mSyncPromoController.setUpSyncPromoView(
                mProfileDataCache, syncPromoView, this::onPromoDismissClicked);
    }

    public void onPromoDismissClicked() {
        setupPromoHidden();
    }

    // SignInAllowedObserver implementation.
    @Override
    public void onSignInAllowedChanged() {
        update();
    }

    // ProfileDataCache.Observer implementation.
    @Override
    public void onProfileDataUpdated(String accountEmail) {
        update();
    }

    // AccountsChangeObserver implementation.
    @Override
    public void onCoreAccountInfosChanged() {
        update();
    }
}