chromium/chrome/browser/password_edit_dialog/android/java/src/org/chromium/chrome/browser/password_edit_dialog/PasswordEditDialogView.java

// Copyright 2022 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.password_edit_dialog;

import android.content.Context;
import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;

import org.chromium.base.Callback;
import org.chromium.ui.text.EmptyTextWatcher;

import java.util.List;

/**
 * The view which represents a username label and input control and a password label and input
 * control. The view has the functionality of editing both password and username.
 */
class PasswordEditDialogView extends LinearLayout {
    private AutoCompleteTextView mUsernameView;
    private TextInputLayout mUsernameInputLayout;
    private TextInputEditText mPasswordField;
    private TextInputLayout mPasswordInputLayout;
    private Callback<String> mUsernameChangedCallback;
    private Callback<String> mPasswordChangedCallback;
    private List<String> mUsernames;
    private TextView mFooterView;

    public PasswordEditDialogView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /** Stores references to the dialog fields after dialog inflation. */
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mFooterView = findViewById(R.id.footer);
        mUsernameView = findViewById(R.id.username_view);
        mUsernameInputLayout = findViewById(R.id.username_input_layout);
        mUsernameInputLayout.setEndIconOnClickListener(view -> mUsernameView.showDropDown());
        mUsernameView.addTextChangedListener(
                new EmptyTextWatcher() {
                    @Override
                    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                        if (mUsernameChangedCallback == null) return;
                        mUsernameChangedCallback.onResult(charSequence.toString());
                    }

                    @Override
                    public void afterTextChanged(Editable editable) {
                        setDropDownVisibility(editable.toString());
                    }
                });

        mPasswordField = findViewById(R.id.password);
        mPasswordField.setInputType(
                InputType.TYPE_CLASS_TEXT
                        | InputType.TYPE_TEXT_VARIATION_PASSWORD
                        | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
        mPasswordField.addTextChangedListener(
                new TextWatcher() {
                    @Override
                    public void beforeTextChanged(
                            CharSequence charSequence, int i, int i1, int i2) {}

                    @Override
                    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                        if (mPasswordChangedCallback == null) return;
                        mPasswordChangedCallback.onResult(charSequence.toString());
                    }

                    @Override
                    public void afterTextChanged(Editable editable) {}
                });
        mPasswordInputLayout = findViewById(R.id.password_text_input_layout);
    }

    public void setPassword(String password) {
        if (mPasswordField.getText().toString().equals(password)) return;
        mPasswordField.setText(password);
    }

    public void setPasswordChangedCallback(Callback<String> callback) {
        mPasswordChangedCallback = callback;
    }

    public void setPasswordError(String error) {
        mPasswordInputLayout.setError(error);
    }

    /** Sets usernames list in the AutoCompleteTextView */
    public void setUsernames(List<String> usernames) {
        mUsernames = usernames;
        ArrayAdapter<String> usernamesAdapter =
                new NoFilterArrayAdapter<>(
                        getContext(), R.layout.password_edit_dialog_dropdown_item, usernames);
        mUsernameView.setAdapter(usernamesAdapter);
        setDropDownVisibility(mUsernameView.getText().toString());
    }

    /**
     * Sets callback for handling username change.
     *
     * @param callback The callback to be called with new username typed into the
     *     AutoCompleteTextView
     */
    public void setUsernameChangedCallback(Callback<String> callback) {
        mUsernameChangedCallback = callback;
    }

    /** Sets username in the text input */
    public void setUsername(String username) {
        if (mUsernameView.getText().toString().equals(username)) return;
        mUsernameView.setText(username);
        setDropDownVisibility(username);
    }

    private void setDropDownVisibility(String currentUsername) {
        if (shouldShowDropDown(currentUsername)) {
            mUsernameInputLayout.setEndIconVisible(true);
        } else {
            // Hide the dropdown button and dismiss the dropdown (in case if it's open).
            mUsernameInputLayout.setEndIconVisible(false);
            mUsernameView.dismissDropDown();
        }
    }

    private boolean shouldShowDropDown(String currentUsername) {
        // Do not show the dropdown, when there are no usernames to list.
        if (mUsernames == null) return false;
        // Show the dropdown when there is more than one choice.
        if (mUsernames.size() > 1) return true;
        // Show the dropdown when there is one choice which is different from current username.
        return (mUsernames.size() == 1 && !mUsernames.get(0).equals(currentUsername));
    }

    void setFooter(String footer) {
        mFooterView.setVisibility(!TextUtils.isEmpty(footer) ? View.VISIBLE : View.GONE);
        mFooterView.setText(footer);
    }
}