chromium/chrome/android/java/src/org/chromium/chrome/browser/permissions/PermissionBlockedDialog.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.permissions;

import android.content.Context;
import android.os.Bundle;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.view.ContextThemeWrapper;
import android.widget.TextView;

import androidx.annotation.NonNull;

import org.jni_zero.CalledByNative;
import org.jni_zero.JniType;
import org.jni_zero.NativeMethods;

import org.chromium.chrome.R;
import org.chromium.chrome.browser.settings.SettingsLauncherFactory;
import org.chromium.components.browser_ui.settings.SettingsLauncher;
import org.chromium.components.browser_ui.site_settings.SingleCategorySettings;
import org.chromium.components.browser_ui.site_settings.SiteSettingsCategory;
import org.chromium.components.content_settings.ContentSettingsType;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.modaldialog.DialogDismissalCause;
import org.chromium.ui.modaldialog.ModalDialogManager;
import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
import org.chromium.ui.modaldialog.ModalDialogProperties;
import org.chromium.ui.modaldialog.ModalDialogProperties.ButtonType;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.text.NoUnderlineClickableSpan;

/**
 * Dialog triggered by the user clicking on the "manage" button in the Messages 2.0 flavor of quiet
 * permission prompt for notifications and geolocation.
 */
public class PermissionBlockedDialog implements ModalDialogProperties.Controller {
    private final ModalDialogManager mModalDialogManager;
    private final Context mContext;
    private long mNativeDialogController;
    private PropertyModel mPropertyModel;

    @CalledByNative
    private static PermissionBlockedDialog create(
            long nativeDialogController, @NonNull WindowAndroid windowAndroid) {
        return new PermissionBlockedDialog(nativeDialogController, windowAndroid);
    }

    public PermissionBlockedDialog(long nativeDialogController, WindowAndroid windowAndroid) {
        mNativeDialogController = nativeDialogController;
        mContext = windowAndroid.getActivity().get();

        mModalDialogManager = windowAndroid.getModalDialogManager();
    }

    @CalledByNative
    void show(
            @JniType("std::u16string") String title,
            @JniType("std::u16string") String content,
            @JniType("std::u16string") String positiveButtonLabel,
            @JniType("std::u16string") String negativeButtonLabel,
            @JniType("std::u16string") String learnMoreText) {
        SpannableStringBuilder fullString = new SpannableStringBuilder();

        TextView message =
                new TextView(
                        new ContextThemeWrapper(
                                mContext, R.style.NotificationBlockedDialogContent));
        fullString.append(content);
        if (!learnMoreText.isEmpty()) {
            fullString.append(" ");
            int start = fullString.length();
            fullString.append(learnMoreText);
            fullString.setSpan(
                    new NoUnderlineClickableSpan(
                            mContext,
                            (v) -> {
                              PermissionBlockedDialogJni.get()
                                        .onLearnMoreClicked(mNativeDialogController);
                            }),
                    start,
                    fullString.length(),
                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        }

        message.setText(fullString);
        message.setMovementMethod(LinkMovementMethod.getInstance());

        mPropertyModel =
                new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
                        .with(ModalDialogProperties.CONTROLLER, this)
                        .with(ModalDialogProperties.TITLE, title)
                        .with(ModalDialogProperties.CUSTOM_VIEW, message)
                        .with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, positiveButtonLabel)
                        .with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, negativeButtonLabel)
                        .with(ModalDialogProperties.CANCEL_ON_TOUCH_OUTSIDE, true)
                        .build();
        mModalDialogManager.showDialog(mPropertyModel, ModalDialogType.APP);
    }

    @Override
    public void onClick(PropertyModel model, @ButtonType int buttonType) {
        if (buttonType == ButtonType.POSITIVE) {
          PermissionBlockedDialogJni.get().onPrimaryButtonClicked(mNativeDialogController);
            mModalDialogManager.dismissDialog(
                    mPropertyModel, DialogDismissalCause.POSITIVE_BUTTON_CLICKED);
        } else if (buttonType == ButtonType.NEGATIVE) {
          PermissionBlockedDialogJni.get().onNegativeButtonClicked(mNativeDialogController);
            mModalDialogManager.dismissDialog(
                    mPropertyModel, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED);
        }
    }

    @Override
    public void onDismiss(PropertyModel model, int dismissalCause) {
      PermissionBlockedDialogJni.get().onDialogDismissed(mNativeDialogController);
        mNativeDialogController = 0;
    }

    @CalledByNative
    private void dismissDialog() {
        mModalDialogManager.dismissDialog(mPropertyModel, DialogDismissalCause.DISMISSED_BY_NATIVE);
    }

    /**
     * This method launches the site settings screen
     *
     * @param contentSettingsType The ContentSettingsType to which the site settings screen should
     *     navigate to.
     */
    @CalledByNative
    private void showSettings(int contentSettingsType) {
        int preferenceKey = 0;
        switch (contentSettingsType) {
            case ContentSettingsType.NOTIFICATIONS:
                preferenceKey = SiteSettingsCategory.Type.NOTIFICATIONS;
                break;
            case ContentSettingsType.GEOLOCATION:
                preferenceKey = SiteSettingsCategory.Type.DEVICE_LOCATION;
                break;
            default:
                assert false : "Should not be reached";
        }
        Bundle fragmentArguments = new Bundle();
        fragmentArguments.putString(
                SingleCategorySettings.EXTRA_CATEGORY,
                SiteSettingsCategory.preferenceKey(preferenceKey));
        SettingsLauncher settingsLauncher = SettingsLauncherFactory.createSettingsLauncher();
        settingsLauncher.launchSettingsActivity(
                mContext, SingleCategorySettings.class, fragmentArguments);
    }

    @NativeMethods
    interface Natives {
        void onPrimaryButtonClicked(long nativePermissionBlockedDialogController);

        void onNegativeButtonClicked(long nativePermissionBlockedDialogController);

        void onLearnMoreClicked(long nativePermissionBlockedDialogController);

        void onDialogDismissed(long nativePermissionBlockedDialogController);
    }
}