chromium/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/OutdatedGmsCoreDialog.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_manager;

import android.content.Context;

import androidx.annotation.IntDef;

import org.chromium.base.Callback;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.ui.modaldialog.DialogDismissalCause;
import org.chromium.ui.modaldialog.ModalDialogManager;
import org.chromium.ui.modaldialog.ModalDialogProperties;
import org.chromium.ui.modaldialog.SimpleModalDialogController;
import org.chromium.ui.modelutil.PropertyModel;

/**
 * Dialog that informs user that the GMS Core needs to be updated and confirms whether the user
 * agrees to proceed with the update.
 */
class OutdatedGmsCoreDialog {
    static final String DISMISSAL_REASON_HISTOGRAM =
            "PasswordManager.OutdatedGMSDialogDismissalReason";
    private final ModalDialogManager mModalDialogManager;
    private final Context mContext;
    private final Callback<Boolean> mResultHandler;

    // These values are persisted to logs. Entries should not be renumbered and
    // numeric values should never be reused.
    @IntDef({
        GmsUpdateDialogDismissalReason.OTHER,
        GmsUpdateDialogDismissalReason.ACCEPTED,
        GmsUpdateDialogDismissalReason.REJECTED,
        GmsUpdateDialogDismissalReason.COUNT
    })
    @interface GmsUpdateDialogDismissalReason {
        int OTHER = 0;
        int ACCEPTED = 1;
        int REJECTED = 2;

        int COUNT = 3;
    }

    /**
     * Constructor for {@link OutdatedGmsCoreDialog}.
     *
     * @param modalDialogManager The {@link ModalDialogManager} which is going to display the
     *         dialog.
     * @param context The context for accessing resources.
     * @param resultHandler Handler to be called on whether the user have chosen to update or not.
     */
    OutdatedGmsCoreDialog(
            ModalDialogManager modalDialogManager,
            Context context,
            Callback<Boolean> resultHandler) {
        mModalDialogManager = modalDialogManager;
        mContext = context;
        mResultHandler = resultHandler;
    }

    /** Shows the dialog. */
    void show() {
        SimpleModalDialogController modalDialogController =
                new SimpleModalDialogController(mModalDialogManager, this::onDismissedWithReason);

        PropertyModel dialogModel =
                new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
                        .with(ModalDialogProperties.CONTROLLER, modalDialogController)
                        .with(
                                ModalDialogProperties.MESSAGE_PARAGRAPH_1,
                                mContext.getResources()
                                        .getString(
                                                R.string
                                                        .password_manager_outdated_gms_dialog_description))
                        .with(
                                ModalDialogProperties.POSITIVE_BUTTON_TEXT,
                                mContext.getResources()
                                        .getString(
                                                R.string
                                                        .password_manager_outdated_gms_positive_button))
                        .with(
                                ModalDialogProperties.NEGATIVE_BUTTON_TEXT,
                                mContext.getResources()
                                        .getString(
                                                R.string
                                                        .password_manager_outdated_gms_negative_button))
                        .with(
                                ModalDialogProperties.BUTTON_STYLES,
                                ModalDialogProperties.ButtonStyles.PRIMARY_FILLED_NEGATIVE_OUTLINE)
                        .build();

        mModalDialogManager.showDialog(dialogModel, ModalDialogManager.ModalDialogType.APP);
    }

    private void onDismissedWithReason(int dismissalReason) {
        // The dismissal reason should be recorded before the result handler is called, because
        // the latter might open a new activity, which can cause the current one to be destroyed.
        RecordHistogram.recordEnumeratedHistogram(
                DISMISSAL_REASON_HISTOGRAM,
                getDismissalReasonForMetrics(dismissalReason),
                GmsUpdateDialogDismissalReason.COUNT);
        mResultHandler.onResult(dismissalReason == DialogDismissalCause.POSITIVE_BUTTON_CLICKED);
    }

    private @GmsUpdateDialogDismissalReason int getDismissalReasonForMetrics(
            int dialogDismissalReason) {
        if (dialogDismissalReason == DialogDismissalCause.POSITIVE_BUTTON_CLICKED) {
            return GmsUpdateDialogDismissalReason.ACCEPTED;
        }
        if (dialogDismissalReason == DialogDismissalCause.NEGATIVE_BUTTON_CLICKED
                || dialogDismissalReason == DialogDismissalCause.NAVIGATE_BACK_OR_TOUCH_OUTSIDE) {
            return GmsUpdateDialogDismissalReason.REJECTED;
        }
        return GmsUpdateDialogDismissalReason.OTHER;
    }
}