// 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.privacy_sandbox;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Handler;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.components.browser_ui.widget.ChromeDialog;
import org.chromium.ui.text.NoUnderlineClickableSpan;
import org.chromium.ui.text.SpanApplier;
import org.chromium.ui.widget.ButtonCompat;
import org.chromium.ui.widget.CheckableImageView;
import org.chromium.ui.widget.TextViewWithLeading;
/** Dialog in the form of a consent shown for the Privacy Sandbox. */
public class PrivacySandboxDialogConsentEEA extends ChromeDialog
implements View.OnClickListener, DialogInterface.OnShowListener {
private static final int SPINNER_DURATION_MS = 1500;
private static final int BACKGROUND_TRANSITION_DURATION_MS = 300;
private final PrivacySandboxBridge mPrivacySandboxBridge;
private View mContentView;
private final CheckableImageView mExpandArrowView;
private LinearLayout mDropdownContainer;
private LinearLayout mDropdownElement;
private LinearLayout mProgressBarContainer;
private LinearLayout mConsentViewContainer;
private ButtonCompat mMoreButton;
private LinearLayout mActionButtons;
private ScrollView mScrollView;
private TextViewWithLeading mLearnMoreText;
private int mSurfaceType;
private boolean mAreAnimationsDisabled;
public PrivacySandboxDialogConsentEEA(
Context context,
PrivacySandboxBridge privacySandboxBridge,
boolean disableAnimations,
@SurfaceType int surfaceType) {
super(context, R.style.ThemeOverlay_BrowserUI_Fullscreen);
mPrivacySandboxBridge = privacySandboxBridge;
mAreAnimationsDisabled = disableAnimations;
mSurfaceType = surfaceType;
mContentView =
LayoutInflater.from(context).inflate(R.layout.privacy_sandbox_consent_eea, null);
setContentView(mContentView);
ButtonCompat ackButton = mContentView.findViewById(R.id.ack_button);
ackButton.setOnClickListener(this);
ButtonCompat noButton = mContentView.findViewById(R.id.no_button);
noButton.setOnClickListener(this);
mMoreButton = mContentView.findViewById(R.id.more_button);
mActionButtons = mContentView.findViewById(R.id.action_buttons);
mScrollView = mContentView.findViewById(R.id.privacy_sandbox_dialog_scroll_view);
mProgressBarContainer = mContentView.findViewById(R.id.progress_bar_container);
mConsentViewContainer = mContentView.findViewById(R.id.privacy_sandbox_consent_eea_view);
// Controls for the expanding section.
mDropdownElement = mContentView.findViewById(R.id.dropdown_element);
mDropdownElement.setOnClickListener(this);
mDropdownContainer = mContentView.findViewById(R.id.dropdown_container);
mExpandArrowView = mContentView.findViewById(R.id.expand_arrow);
mExpandArrowView.setImageDrawable(PrivacySandboxDialogUtils.createExpandDrawable(context));
mExpandArrowView.setChecked(isDropdownExpanded());
mMoreButton.setOnClickListener(this);
setOnShowListener(this);
setCancelable(false);
mScrollView
.getViewTreeObserver()
.addOnScrollChangedListener(
() -> {
if (!mScrollView.canScrollVertically(ScrollView.FOCUS_DOWN)) {
mMoreButton.setVisibility(View.GONE);
mActionButtons.setVisibility(View.VISIBLE);
mScrollView.post(
() -> {
mScrollView.pageScroll(ScrollView.FOCUS_DOWN);
});
}
});
}
@Override
public void show() {
mPrivacySandboxBridge.promptActionOccurred(PromptAction.CONSENT_SHOWN, mSurfaceType);
super.show();
}
// OnClickListener:
@Override
public void onClick(View view) {
int id = view.getId();
if (id == R.id.ack_button) {
mPrivacySandboxBridge.promptActionOccurred(PromptAction.CONSENT_ACCEPTED, mSurfaceType);
dismissAndMaybeShowNotice();
} else if (id == R.id.no_button) {
mPrivacySandboxBridge.promptActionOccurred(PromptAction.CONSENT_DECLINED, mSurfaceType);
dismissAndMaybeShowNotice();
} else if (id == R.id.more_button) {
mPrivacySandboxBridge.promptActionOccurred(
PromptAction.CONSENT_MORE_BUTTON_CLICKED, mSurfaceType);
if (mScrollView.canScrollVertically(ScrollView.FOCUS_DOWN)) {
mScrollView.post(
() -> {
mScrollView.pageScroll(ScrollView.FOCUS_DOWN);
});
} else {
mMoreButton.setVisibility(View.GONE);
mActionButtons.setVisibility(View.VISIBLE);
mScrollView.post(
() -> {
mScrollView.pageScroll(ScrollView.FOCUS_DOWN);
});
}
} else if (id == R.id.dropdown_element) {
if (isDropdownExpanded()) {
mPrivacySandboxBridge.promptActionOccurred(
PromptAction.CONSENT_MORE_INFO_CLOSED, mSurfaceType);
mDropdownContainer.setVisibility(View.GONE);
mDropdownContainer.removeAllViews();
mScrollView.post(
() -> {
mScrollView.fullScroll(ScrollView.FOCUS_DOWN);
});
} else {
mDropdownContainer.setVisibility(View.VISIBLE);
mPrivacySandboxBridge.promptActionOccurred(
PromptAction.CONSENT_MORE_INFO_OPENED, mSurfaceType);
LayoutInflater.from(getContext())
.inflate(R.layout.privacy_sandbox_consent_eea_dropdown, mDropdownContainer);
PrivacySandboxDialogUtils.setBulletTextWithBoldContent(
getContext(),
mDropdownContainer,
R.id.privacy_sandbox_m1_consent_learn_more_bullet_one,
R.string.privacy_sandbox_m1_consent_learn_more_bullet_1);
PrivacySandboxDialogUtils.setBulletTextWithBoldContent(
getContext(),
mDropdownContainer,
R.id.privacy_sandbox_m1_consent_learn_more_bullet_two,
R.string.privacy_sandbox_m1_consent_learn_more_bullet_2);
PrivacySandboxDialogUtils.setBulletTextWithBoldContent(
getContext(),
mDropdownContainer,
R.id.privacy_sandbox_m1_consent_learn_more_bullet_three,
R.string.privacy_sandbox_m1_consent_learn_more_bullet_3);
mScrollView.post(
() -> {
mScrollView.scrollTo(0, mDropdownElement.getTop());
});
mLearnMoreText = mContentView.findViewById(R.id.privacy_sandbox_learn_more_text);
if (ChromeFeatureList.isEnabled(ChromeFeatureList.PRIVACY_SANDBOX_PRIVACY_POLICY)) {
mLearnMoreText.setText(
SpanApplier.applySpans(
getContext()
.getResources()
.getString(
R.string
.privacy_sandbox_m1_notice_learn_more_v2_clank),
new SpanApplier.SpanInfo(
"<link>",
"</link>",
new NoUnderlineClickableSpan(
getContext(), this::onPrivacyPolicyClicked))));
mLearnMoreText.setMovementMethod(LinkMovementMethod.getInstance());
}
}
mExpandArrowView.setChecked(isDropdownExpanded());
PrivacySandboxDialogUtils.updateDropdownControlContentDescription(
getContext(),
view,
isDropdownExpanded(),
R.string.privacy_sandbox_m1_consent_learn_more_expand_label);
view.announceForAccessibility(
getContext()
.getResources()
.getString(
isDropdownExpanded()
? R.string.accessibility_expanded_group
: R.string.accessibility_collapsed_group));
}
}
@Override
public void onShow(DialogInterface dialogInterface) {
if (mScrollView.canScrollVertically(ScrollView.FOCUS_DOWN)) {
mMoreButton.setVisibility(View.VISIBLE);
mActionButtons.setVisibility(View.GONE);
} else {
mMoreButton.setVisibility(View.GONE);
mActionButtons.setVisibility(View.VISIBLE);
}
}
private void onPrivacyPolicyClicked(View view) {
// TODO: Open ThinWebView
}
private void dismissAndMaybeShowNotice() {
mProgressBarContainer.setVisibility(View.VISIBLE);
mConsentViewContainer.setVisibility(View.GONE);
var consentHandler = new Handler();
// Dismiss has a bigger timeout than spinner in order to guarantee a graceful transition
// between the spinner view and the notice one.
consentHandler.postDelayed(this::dismiss, getDismissTimeout());
consentHandler.postDelayed(this::showNotice, getSpinnerTimeout());
}
private void showNotice() {
PrivacySandboxDialogController.showNoticeEEA(
getContext(), mPrivacySandboxBridge, mSurfaceType);
}
private boolean isDropdownExpanded() {
return mDropdownContainer != null && mDropdownContainer.getVisibility() == View.VISIBLE;
}
private long getDismissTimeout() {
return mAreAnimationsDisabled ? 0 : SPINNER_DURATION_MS + BACKGROUND_TRANSITION_DURATION_MS;
}
private long getSpinnerTimeout() {
return mAreAnimationsDisabled ? 0 : SPINNER_DURATION_MS;
}
public int getSurfaceTypeForTesting() {
return mSurfaceType;
}
}