chromium/chrome/browser/password_manager/android/add_username_dialog/java/src/org/chromium/chrome/browser/add_username_dialog/AddUsernameDialogController.java

// Copyright 2023 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.add_username_dialog;

import static org.chromium.chrome.browser.add_username_dialog.AddUsernameDialogContentProperties.PASSWORD;
import static org.chromium.chrome.browser.add_username_dialog.AddUsernameDialogContentProperties.USERNAME;
import static org.chromium.chrome.browser.add_username_dialog.AddUsernameDialogContentProperties.USERNAME_CHANGED_CALLBACK;

import android.content.Context;
import android.content.res.Resources;
import android.view.LayoutInflater;

import androidx.annotation.NonNull;

import org.chromium.chrome.browser.password_manager.PasswordManagerResourceProvider;
import org.chromium.chrome.browser.password_manager.PasswordManagerResourceProviderFactory;
import org.chromium.ui.modaldialog.DialogDismissalCause;
import org.chromium.ui.modaldialog.ModalDialogManager;
import org.chromium.ui.modaldialog.ModalDialogProperties;
import org.chromium.ui.modaldialog.ModalDialogProperties.ButtonType;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor;

/**
 * Coordinator for the add username dialog. The dialog is displayed when the password is being
 * automatically saved and the username could not be captured from the password form. It allows user
 * to add the username for the saved password.
 */
public class AddUsernameDialogController implements ModalDialogProperties.Controller {
    /** A delegate interface to receive the results of the dialog interaction. */
    interface Delegate {
        /**
         * Called when the user taps the dialog positive button.
         *
         * @param username The username that will be saved in the password store.
         */
        void onDialogAccepted(String username);

        /** Called when the dialog is dismissed. */
        void onDialogDismissed();
    }

    private final Context mContext;
    private final ModalDialogManager mModalDialogManager;
    private final Delegate mDelegate;
    private PropertyModel mModalDialogModel;
    private final AddUsernameDialogContentView mContentView;
    private PropertyModel mContentViewModel;

    /**
     * Creates the {@link AddUsernameDialogController}.
     *
     * @param context The context for accessing resources.
     * @param modalDialogManager The manager for displaying the modal dialog.
     * @param delegate The delegate to be called with the results of the interaction.
     */
    public AddUsernameDialogController(
            Context context, ModalDialogManager modalDialogManager, @NonNull Delegate delegate) {
        mContext = context;
        mModalDialogManager = modalDialogManager;
        mDelegate = delegate;
        mContentView =
                (AddUsernameDialogContentView)
                        LayoutInflater.from(context)
                                .inflate(R.layout.add_username_dialog_content, null);
    }

    public void showAddUsernameDialog(String password) {
        mContentViewModel = createContentViewModel(password);
        PropertyModelChangeProcessor.create(
                mContentViewModel, mContentView, AddUsernameDialogContentViewBinder::bind);

        mModalDialogModel = createModalDialogModel();
        mModalDialogManager.showDialog(mModalDialogModel, ModalDialogManager.ModalDialogType.APP);
    }

    private PropertyModel createContentViewModel(String password) {
        return new PropertyModel.Builder(AddUsernameDialogContentProperties.ALL_KEYS)
                .with(PASSWORD, password)
                .with(USERNAME, "")
                .with(USERNAME_CHANGED_CALLBACK, this::onUsernameChanged)
                .build();
    }

    private PropertyModel createModalDialogModel() {
        Resources resources = mContext.getResources();
        PasswordManagerResourceProvider resourceProvider =
                PasswordManagerResourceProviderFactory.create();
        PropertyModel.Builder dialogModeBuilder =
                new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
                        .with(ModalDialogProperties.CONTROLLER, this)
                        .with(ModalDialogProperties.CUSTOM_VIEW, mContentView)
                        .with(
                                ModalDialogProperties.TITLE,
                                resources,
                                R.string.add_username_dialog_title)
                        .with(
                                ModalDialogProperties.POSITIVE_BUTTON_TEXT,
                                resources,
                                R.string.add_username_dialog_add_username)
                        .with(
                                ModalDialogProperties.NEGATIVE_BUTTON_TEXT,
                                resources,
                                R.string.add_username_dialog_cancel)
                        .with(
                                ModalDialogProperties.BUTTON_STYLES,
                                ModalDialogProperties.ButtonStyles.PRIMARY_FILLED_NEGATIVE_OUTLINE)
                        .with(
                                ModalDialogProperties.TITLE_ICON,
                                mContext,
                                resourceProvider.getPasswordManagerIcon());
        return dialogModeBuilder.build();
    }

    public void dismissDialog() {
        mModalDialogManager.dismissDialog(
                mModalDialogModel, DialogDismissalCause.DISMISSED_BY_NATIVE);
    }

    private void onUsernameChanged(String username) {
        mContentViewModel.set(AddUsernameDialogContentProperties.USERNAME, username);
    }

    // ModalDialogProperties.Controller implementation.
    @Override
    public void onClick(PropertyModel model, @ButtonType int buttonType) {
        if (buttonType == ButtonType.POSITIVE) {
            mDelegate.onDialogAccepted(
                    mContentViewModel.get(AddUsernameDialogContentProperties.USERNAME));
        }
        mModalDialogManager.dismissDialog(
                model,
                buttonType == ButtonType.POSITIVE
                        ? DialogDismissalCause.POSITIVE_BUTTON_CLICKED
                        : DialogDismissalCause.NEGATIVE_BUTTON_CLICKED);
    }

    @Override
    public void onDismiss(PropertyModel model, @DialogDismissalCause int dismissalCause) {
        mDelegate.onDialogDismissed();
    }
}