chromium/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DangerousDownloadDialog.java

// Copyright 2021 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.download.dialogs;

import android.content.Context;

import androidx.annotation.IntDef;
import androidx.core.content.res.ResourcesCompat;

import org.chromium.base.Callback;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.browser.download.R;
import org.chromium.components.browser_ui.util.DownloadUtils;
import org.chromium.ui.UiUtils;
import org.chromium.ui.modaldialog.DialogDismissalCause;
import org.chromium.ui.modaldialog.ModalDialogManager;
import org.chromium.ui.modaldialog.ModalDialogProperties;
import org.chromium.ui.modelutil.PropertyModel;

/**
 * Dialog for confirming that user want to download a dangerous file, using the default model dialog
 * from ModalDialogManager.
 */
public class DangerousDownloadDialog {
    /**
     * Events related to the dangerous download dialog, used for UMA reporting.
     * These values are persisted to logs. Entries should not be renumbered and
     * numeric values should never be reused.
     */
    @IntDef({
        DangerousDownloadDialogEvent.DANGEROUS_DOWNLOAD_DIALOG_SHOW,
        DangerousDownloadDialogEvent.DANGEROUS_DOWNLOAD_DIALOG_CONFIRM,
        DangerousDownloadDialogEvent.DANGEROUS_DOWNLOAD_DIALOG_CANCEL,
        DangerousDownloadDialogEvent.DANGEROUS_DOWNLOAD_DIALOG_DISMISS
    })
    private @interface DangerousDownloadDialogEvent {
        int DANGEROUS_DOWNLOAD_DIALOG_SHOW = 0;
        int DANGEROUS_DOWNLOAD_DIALOG_CONFIRM = 1;
        int DANGEROUS_DOWNLOAD_DIALOG_CANCEL = 2;
        int DANGEROUS_DOWNLOAD_DIALOG_DISMISS = 3;

        int COUNT = 4;
    }

    public DangerousDownloadDialog() {}

    /**
     * Called to show a warning dialog for dangerous download.
     * @param context Context for showing the dialog.
     * @param modalDialogManager Manager for managing the modal dialog.
     * @param fileName Name of the download file.
     * @param totalBytes Total bytes of the file.
     * @param iconId Icon ID of the warning dialog.
     * @param callback Callback to run when confirming the download, true for accept the download,
     *         false otherwise.
     */
    public void show(
            Context context,
            ModalDialogManager modalDialogManager,
            String fileName,
            long totalBytes,
            int iconId,
            Callback<Boolean> callback) {
        var resources = context.getResources();
        String message =
                totalBytes > 0
                        ? resources.getString(R.string.dangerous_download_dialog_text, fileName)
                        : resources.getString(
                                R.string.dangerous_download_dialog_text_with_size,
                                fileName,
                                DownloadUtils.getStringForBytes(context, totalBytes));

        var controller =
                new ModalDialogProperties.Controller() {
                    @Override
                    public void onClick(PropertyModel model, int buttonType) {
                        boolean acceptDownload =
                                buttonType == ModalDialogProperties.ButtonType.POSITIVE;
                        if (callback != null) {
                            callback.onResult(acceptDownload);
                        }
                        modalDialogManager.dismissDialog(
                                model,
                                acceptDownload
                                        ? DialogDismissalCause.POSITIVE_BUTTON_CLICKED
                                        : DialogDismissalCause.NEGATIVE_BUTTON_CLICKED);
                        recordDangerousDownloadDialogEvent(
                                acceptDownload
                                        ? DangerousDownloadDialogEvent
                                                .DANGEROUS_DOWNLOAD_DIALOG_CONFIRM
                                        : DangerousDownloadDialogEvent
                                                .DANGEROUS_DOWNLOAD_DIALOG_CANCEL);
                    }

                    @Override
                    public void onDismiss(PropertyModel model, int dismissalCause) {
                        if (dismissalCause != DialogDismissalCause.POSITIVE_BUTTON_CLICKED
                                && dismissalCause != DialogDismissalCause.NEGATIVE_BUTTON_CLICKED) {
                            if (callback != null) callback.onResult(false);
                            recordDangerousDownloadDialogEvent(
                                    DangerousDownloadDialogEvent.DANGEROUS_DOWNLOAD_DIALOG_DISMISS);
                        }
                    }
                };
        PropertyModel propertyModel =
                new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
                        .with(ModalDialogProperties.CONTROLLER, controller)
                        .with(
                                ModalDialogProperties.TITLE,
                                resources.getString(R.string.dangerous_download_dialog_title))
                        .with(ModalDialogProperties.MESSAGE_PARAGRAPH_1, message)
                        .with(
                                ModalDialogProperties.POSITIVE_BUTTON_TEXT,
                                resources.getString(
                                        R.string.dangerous_download_dialog_confirm_text))
                        .with(
                                ModalDialogProperties.NEGATIVE_BUTTON_TEXT,
                                resources.getString(R.string.cancel))
                        .with(
                                ModalDialogProperties.TITLE_ICON,
                                ResourcesCompat.getDrawable(resources, iconId, context.getTheme()))
                        .with(
                                ModalDialogProperties.BUTTON_STYLES,
                                ModalDialogProperties.ButtonStyles.PRIMARY_OUTLINE_NEGATIVE_OUTLINE)
                        .with(
                                ModalDialogProperties.BUTTON_TAP_PROTECTION_PERIOD_MS,
                                UiUtils.PROMPT_INPUT_PROTECTION_SHORT_DELAY_MS)
                        .build();

        modalDialogManager.showDialog(propertyModel, ModalDialogManager.ModalDialogType.TAB);
        recordDangerousDownloadDialogEvent(
                DangerousDownloadDialogEvent.DANGEROUS_DOWNLOAD_DIALOG_SHOW);
    }

    /**
     * Collects dangerous download dialog UI event metrics.
     *
     * @param event The UI event to collect.
     */
    private static void recordDangerousDownloadDialogEvent(
            @DangerousDownloadDialogEvent int event) {
        RecordHistogram.recordEnumeratedHistogram(
                "Download.DangerousDialog.Events", event, DangerousDownloadDialogEvent.COUNT);
    }
}