behavior = bottomSheetDialog.getBehavior();
+ behavior.setSkipCollapsed(true);
+ // Defer expanding by one frame. Expanding synchronously from the show listener can run
+ // before the landscape system-bar insets are dispatched, so BottomSheetBehavior lays the
+ // sheet out edge-to-edge (insetLeft == 0) and its content is clipped under the navigation
+ // bar. By the next frame the insets are applied and the expanded sheet is offset correctly.
+ final View content = getView();
+ if (content != null) {
+ content.post(() -> behavior.setState(BottomSheetBehavior.STATE_EXPANDED));
+ } else {
+ behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
+ }
+ }
+
@SuppressLint("ClickableViewAccessibility")
@Override
public View onCreateView(
diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/WalletBottomSheetDialogFragment.java b/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/WalletBottomSheetDialogFragment.java
index 65241a60769cb..db04e6d521909 100644
--- a/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/WalletBottomSheetDialogFragment.java
+++ b/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/WalletBottomSheetDialogFragment.java
@@ -5,9 +5,17 @@
package org.chromium.chrome.browser.crypto_wallet.fragments;
+import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.View;
+import androidx.core.graphics.Insets;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowInsetsCompat;
+
+import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import org.chromium.brave_wallet.mojom.BraveWalletService;
@@ -16,9 +24,11 @@
import org.chromium.build.annotations.EnsuresNonNull;
import org.chromium.build.annotations.MonotonicNonNull;
import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
import org.chromium.chrome.browser.app.domain.KeyringModel;
import org.chromium.chrome.browser.app.domain.WalletModel;
import org.chromium.chrome.browser.crypto_wallet.observers.KeyringServiceObserverImpl;
+import org.chromium.chrome.browser.util.ConfigurationUtils;
/**
* Base class for {@code BottomSheetDialogFragment} with wallet specific implementation
@@ -77,6 +87,55 @@ public void onAttach(Context context) {
}
}
+ /**
+ * Pads the sheet contents by the display-cutout insets so that, when the sheet is full-width in
+ * landscape, its contents stay clear of a cutout (e.g. a camera hole). The cutout's left/right
+ * insets are already orientation-aware (zero unless the cutout is on a vertical edge), and
+ * tablets keep the centered, width-limited sheet, so the padding is applied for phones only.
+ * {@code BottomSheetBehavior} already handles the system-bar insets.
+ */
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ final int basePaddingLeft = view.getPaddingLeft();
+ final int basePaddingRight = view.getPaddingRight();
+ ViewCompat.setOnApplyWindowInsetsListener(
+ view,
+ (v, insets) -> {
+ int left = basePaddingLeft;
+ int right = basePaddingRight;
+ if (!ConfigurationUtils.isTablet(v.getContext())) {
+ final Insets cutout =
+ insets.getInsets(WindowInsetsCompat.Type.displayCutout());
+ left += cutout.left;
+ right += cutout.right;
+ }
+ v.setPadding(left, v.getPaddingTop(), right, v.getPaddingBottom());
+ return insets;
+ });
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ // Tablets keep Material's default width.
+ if (ConfigurationUtils.isTablet(requireContext())) {
+ return;
+ }
+
+ final Dialog dialog = getDialog();
+ if (!(dialog instanceof BottomSheetDialog bottomSheetDialog)) {
+ return;
+ }
+
+ // Material's BottomSheetDialog caps the sheet width (640dp by default) and
+ // centers it on wide layouts, which leaves side gaps in landscape. On phones
+ // we want the sheet to span the full width.
+ bottomSheetDialog.getBehavior().setMaxWidth(-1);
+ }
+
@Override
public void onDismiss(DialogInterface dialog) {
mKeyringObserver.close();
diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/dapps/SignMessageFragment.java b/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/dapps/SignMessageFragment.java
index 5dbd3842958ed..b9a85e2ed4a36 100644
--- a/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/dapps/SignMessageFragment.java
+++ b/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/dapps/SignMessageFragment.java
@@ -183,7 +183,6 @@ private void maybeHandlePendingRequests(final SignMessageRequest @Nullable [] re
mHasUnicodeWarning = Validations.hasUnicode(message);
if (mHasUnicodeWarning) {
- mSignMessageText.setLines(12);
View view = getView();
if (view != null) {
TextView warningLinkText = view.findViewById(R.id.non_ascii_warning_text_link);
diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/dapps/SignSolTransactionsFragment.java b/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/dapps/SignSolTransactionsFragment.java
index fb1545eaa1e13..7d6cdaef2849b 100644
--- a/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/dapps/SignSolTransactionsFragment.java
+++ b/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/dapps/SignSolTransactionsFragment.java
@@ -9,8 +9,6 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
-import android.text.Spanned;
-import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -57,6 +55,19 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+/**
+ * Bottom sheet for reviewing and approving or rejecting a dApp request to sign one or more Solana
+ * transactions ({@code signTransaction} / {@code signAllTransactions}). The transactions are only
+ * signed here, never broadcast.
+ *
+ * Pending {@link SignSolTransactionsRequest}s are observed from the {@code DappsModel} and shown
+ * as a queue: the counter and "Next" control page through them ("N of M"), and each request is
+ * approved or rejected individually.
+ *
+ *
Each request uses a two-step flow: a risk-acknowledgment panel first (the primary button reads
+ * "Continue"), then the transaction step, which lists the decoded Solana instructions in the
+ * "Details" pager and signs on confirmation.
+ */
@NullMarked
public class SignSolTransactionsFragment extends WalletBottomSheetDialogFragment {
private List mTabTitles;
@@ -128,17 +139,11 @@ public View onCreateView(
private void initComponents() {
updateTxPanelPerStep();
fetchSignRequestData();
- Spanned associatedSPLTokenAccountInfo =
- Utils.createSpanForSurroundedPhrase(
- requireContext(),
- R.string.learn_more,
- (v) -> {
- TabUtils.openUrlInNewTab(
- false, WalletConstants.URL_SIGN_TRANSACTION_REQUEST);
- TabUtils.bringChromeTabbedActivityToTheTop(getActivity());
- });
- mTxLearnMore.setMovementMethod(LinkMovementMethod.getInstance());
- mTxLearnMore.setText(associatedSPLTokenAccountInfo);
+ mTxLearnMore.setOnClickListener(
+ v -> {
+ TabUtils.openUrlInNewTab(false, WalletConstants.URL_SIGN_TRANSACTION_REQUEST);
+ TabUtils.bringChromeTabbedActivityToTheTop(getActivity());
+ });
mBtSign.setOnClickListener(v -> processRequest(true));
mBtCancel.setOnClickListener(v -> processRequest(false));