Merge pull request #14973 from brave/fix-show-multichain-accounts-on-connect-panel

feat(wallet): implement solana dapps connection
This commit is contained in:
Pavneet Singh
2022-09-06 10:22:15 +05:30
committed by GitHub
12 changed files with 184 additions and 95 deletions
@@ -19,11 +19,18 @@ import org.chromium.brave_wallet.mojom.TransactionInfo;
import org.chromium.brave_wallet.mojom.TransactionStatus;
import org.chromium.chrome.browser.crypto_wallet.util.PendingTxHelper;
import org.chromium.chrome.browser.crypto_wallet.util.Utils;
import org.chromium.mojo.bindings.Callbacks;
import org.chromium.mojo.system.Pair;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class DappsModel {
private JsonRpcService mJsonRpcService;
private KeyringService mKeyringService;
private BraveWalletService mBraveWalletService;
private CryptoModel mCryptoModel;
private PendingTxHelper mPendingTxHelper;
private final MutableLiveData<Boolean> _mWalletIconNotificationVisible =
new MutableLiveData<>(false);
@@ -31,12 +38,39 @@ public class DappsModel {
private final Object mLock = new Object();
public DappsModel(JsonRpcService jsonRpcService, BraveWalletService braveWalletService,
PendingTxHelper pendingTxHelper) {
KeyringService keyringService, PendingTxHelper pendingTxHelper) {
mBraveWalletService = braveWalletService;
mJsonRpcService = jsonRpcService;
mKeyringService = keyringService;
mPendingTxHelper = pendingTxHelper;
}
public void fetchAccountsForConnectionReq(@CoinType.EnumType int coinType,
Callbacks.Callback1<Pair<String, List<AccountInfo>>> callback) {
if (coinType == CoinType.ETH || coinType == CoinType.SOL) {
mKeyringService.getKeyringInfo(Utils.getKeyringForCoinType(coinType), keyringInfo -> {
mKeyringService.getSelectedAccount(coinType, accountAddress -> {
if (coinType == CoinType.SOL) {
// only the selected account is used for solana dapps
for (AccountInfo accountInfo : keyringInfo.accountInfos) {
if (accountAddress.equals(accountInfo.address)) {
List<AccountInfo> accountInfos = new ArrayList<>();
accountInfos.add(accountInfo);
callback.call(new Pair<>(accountAddress, accountInfos));
return;
}
}
} else {
callback.call(new Pair<>(
accountAddress, Arrays.asList(keyringInfo.accountInfos)));
}
});
});
} else {
callback.call(new Pair<>(null, Collections.emptyList()));
}
}
public void resetServices(JsonRpcService jsonRpcService,
BraveWalletService braveWalletService, PendingTxHelper pendingTxHelper) {
synchronized (mLock) {
@@ -56,8 +56,8 @@ public class WalletModel {
mCryptoModel = new CryptoModel(mContext, mTxService, mKeyringService, mBlockchainRegistry,
mJsonRpcService, mEthTxManagerProxy, mSolanaTxManagerProxy, mBraveWalletService,
mAssetRatioService, mCryptoActions, mSwapService);
mDappsModel = new DappsModel(
mJsonRpcService, mBraveWalletService, mCryptoModel.getPendingTxHelper());
mDappsModel = new DappsModel(mJsonRpcService, mBraveWalletService, mKeyringService,
mCryptoModel.getPendingTxHelper());
mKeyringModel = new KeyringModel(
mKeyringService, mCryptoModel.getSharedData(), mBraveWalletService, mCryptoActions);
// be careful with dependencies, must avoid cycles
@@ -16,6 +16,7 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -29,6 +30,7 @@ import org.chromium.brave_wallet.mojom.KeyringService;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.app.BraveActivity;
import org.chromium.chrome.browser.app.domain.WalletModel;
import org.chromium.chrome.browser.crypto_wallet.activities.AddAccountActivity;
import org.chromium.chrome.browser.crypto_wallet.activities.BraveWalletBaseActivity;
import org.chromium.chrome.browser.crypto_wallet.fragments.CreateAccountBottomSheetFragment;
@@ -40,8 +42,10 @@ import org.chromium.chrome.browser.ui.favicon.FaviconHelper.DefaultFaviconHelper
import org.chromium.chrome.browser.ui.favicon.FaviconHelper.FaviconImageCallback;
import org.chromium.url.GURL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
public class ConnectAccountFragment extends BaseDAppsFragment
@@ -54,46 +58,42 @@ public class ConnectAccountFragment extends BaseDAppsFragment
private HashSet<AccountInfo> mAccountsWithPermissions;
private BraveEthereumPermissionAccountsListAdapter mAccountsListAdapter;
private RecyclerView mRecyclerView;
private String mSelectedAccount;
private AccountInfo mSelectedAccount;
private FaviconHelper mFaviconHelper;
private DefaultFaviconHelper mDefaultFaviconHelper;
private WalletModel mWalletModel;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BraveActivity activity = BraveActivity.getBraveActivity();
if (activity != null) {
mWalletModel = activity.getWalletModel();
}
}
@SuppressLint("NotifyDataSetChanged")
private void updateAccounts() {
getKeyringService().getSelectedAccount(CoinType.ETH, address -> {
mSelectedAccount = address != null ? address : "";
getKeyringService().getKeyringInfo(
BraveWalletConstants.DEFAULT_KEYRING_ID, keyringInfo -> {
mAccountInfos = new AccountInfo[0];
if (keyringInfo != null) {
mAccountInfos = keyringInfo.accountInfos;
}
AccountsPermissionsHelper accountsPermissionsHelper =
new AccountsPermissionsHelper(getBraveWalletService(),
mAccountInfos, Utils.getCurrentMojomOrigin());
accountsPermissionsHelper.checkAccounts(() -> {
mAccountsWithPermissions =
accountsPermissionsHelper.getAccountsWithPermissions();
mAccountsConnected.setText(String.format(
getResources().getString(R.string.wallet_accounts_connected),
mAccountsWithPermissions.size()));
if (mAccountsListAdapter == null) {
mAccountsListAdapter =
new BraveEthereumPermissionAccountsListAdapter(
mAccountInfos, false, this);
mRecyclerView.setAdapter(mAccountsListAdapter);
LinearLayoutManager layoutManager =
new LinearLayoutManager(getActivity());
mRecyclerView.setLayoutManager(layoutManager);
} else {
mAccountsListAdapter.setAccounts(mAccountInfos);
mAccountsListAdapter.setAccountsWithPermissions(
mAccountsWithPermissions);
mAccountsListAdapter.setSelectedAccount(mSelectedAccount);
mAccountsListAdapter.notifyDataSetChanged();
}
});
});
if (mSelectedAccount == null || mAccountInfos == null) return;
AccountsPermissionsHelper accountsPermissionsHelper = new AccountsPermissionsHelper(
getBraveWalletService(), mAccountInfos, Utils.getCurrentMojomOrigin());
accountsPermissionsHelper.checkAccounts(() -> {
mAccountsWithPermissions = accountsPermissionsHelper.getAccountsWithPermissions();
mAccountsConnected.setText(
String.format(getResources().getString(R.string.wallet_accounts_connected),
mAccountsWithPermissions.size()));
if (mAccountsListAdapter == null) {
mAccountsListAdapter =
new BraveEthereumPermissionAccountsListAdapter(mAccountInfos, false, this);
mRecyclerView.setAdapter(mAccountsListAdapter);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
mRecyclerView.setLayoutManager(layoutManager);
} else {
mAccountsListAdapter.setAccounts(mAccountInfos);
mAccountsListAdapter.setAccountsWithPermissions(mAccountsWithPermissions);
mAccountsListAdapter.setSelectedAccount(mSelectedAccount.address);
mAccountsListAdapter.notifyDataSetChanged();
}
});
}
@@ -134,6 +134,18 @@ public class ConnectAccountFragment extends BaseDAppsFragment
mFaviconHelper.getLocalFaviconImageForURL(
activity.getCurrentProfile(), pageUrl, 0, imageCallback);
}
assert mWalletModel != null;
mWalletModel.getKeyringModel().mAccountAllAccountsPair.observe(
getViewLifecycleOwner(), accountInfoListPair -> {
mSelectedAccount = accountInfoListPair.first;
List<AccountInfo> accountInfos = new ArrayList<>(accountInfoListPair.second);
if (mSelectedAccount != null) {
Utils.removeIf(
accountInfos, account -> account.coin != mSelectedAccount.coin);
}
mAccountInfos = accountInfos.toArray(new AccountInfo[0]);
updateAccounts();
});
}
private void onFaviconAvailable(GURL pageUrl, Bitmap favicon) {
@@ -166,18 +178,18 @@ public class ConnectAccountFragment extends BaseDAppsFragment
@Override
public String getSelectedAccount() {
return mSelectedAccount;
return mSelectedAccount.address;
}
@Override
public void connectAccount(AccountInfo account) {
getBraveWalletService().addPermission(
CoinType.ETH, Utils.getCurrentMojomOrigin(), account.address, success -> {
account.coin, Utils.getCurrentMojomOrigin(), account.address, success -> {
if (!success) {
return;
}
getKeyringService().setSelectedAccount(
account.address, CoinType.ETH, setSuccess -> {
account.address, account.coin, setSuccess -> {
if (setSuccess) {
updateAccounts();
}
@@ -188,11 +200,11 @@ public class ConnectAccountFragment extends BaseDAppsFragment
@Override
public void disconnectAccount(AccountInfo account) {
getBraveWalletService().resetPermission(
CoinType.ETH, Utils.getCurrentMojomOrigin(), account.address, success -> {
account.coin, Utils.getCurrentMojomOrigin(), account.address, success -> {
if (!success) {
return;
}
if (!mSelectedAccount.equals(account.address)) {
if (!mSelectedAccount.address.equals(account.address)) {
updateAccounts();
return;
@@ -202,11 +214,11 @@ public class ConnectAccountFragment extends BaseDAppsFragment
assert mAccountsWithPermissions != null;
Iterator<AccountInfo> it = mAccountsWithPermissions.iterator();
while (it.hasNext()) {
String currentAddress = it.next().address;
if (!currentAddress.equals(account.address)) {
AccountInfo accountInfo = it.next();
if (!accountInfo.address.equals(account.address)) {
updateCalled = true;
getKeyringService().setSelectedAccount(currentAddress, CoinType.ETH,
setSuccess -> { updateAccounts(); });
getKeyringService().setSelectedAccount(accountInfo.address,
accountInfo.coin, setSuccess -> { updateAccounts(); });
break;
}
}
@@ -218,7 +230,7 @@ public class ConnectAccountFragment extends BaseDAppsFragment
@Override
public void switchAccount(AccountInfo account) {
getKeyringService().setSelectedAccount(account.address, CoinType.ETH, setSuccess -> {
getKeyringService().setSelectedAccount(account.address, account.coin, setSuccess -> {
if (setSuccess) {
updateAccounts();
}
@@ -90,6 +90,13 @@ public class BraveEthereumPermissionAccountsListAdapter
public void setSelectedAccount(String selectedAccount) {
mSelectedAccount = selectedAccount;
if (mAccountInfo == null || !mCheckBoxStyle) return;
for (int i = 0; i < mAccountInfo.length; i++) {
if (mSelectedAccount.equals(mAccountInfo[i].address)) {
mCheckedPositions.add(i);
break;
}
}
}
@Override
@@ -100,6 +107,10 @@ public class BraveEthereumPermissionAccountsListAdapter
setBlockiesBitmapResource(holder.iconImg, mAccountInfo[arrayPosition].address);
if (mCheckBoxStyle) {
holder.accountCheck.setVisibility(View.VISIBLE);
if (mSelectedAccount != null
&& mSelectedAccount.equals(mAccountInfo[arrayPosition].address)) {
holder.accountCheck.setChecked(true);
}
holder.accountCheck.setOnCheckedChangeListener(
new CompoundButton.OnCheckedChangeListener() {
@Override
@@ -5,7 +5,7 @@
package org.chromium.chrome.browser.crypto_wallet.permission;
import android.app.Activity;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Rect;
@@ -22,11 +22,12 @@ import androidx.recyclerview.widget.RecyclerView;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.brave_wallet.mojom.AccountInfo;
import org.chromium.brave_wallet.mojom.BraveWalletConstants;
import org.chromium.brave_wallet.mojom.BraveWalletService;
import org.chromium.brave_wallet.mojom.CoinType;
import org.chromium.brave_wallet.mojom.KeyringService;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.app.BraveActivity;
import org.chromium.chrome.browser.app.domain.WalletModel;
import org.chromium.chrome.browser.crypto_wallet.BraveWalletServiceFactory;
import org.chromium.chrome.browser.crypto_wallet.KeyringServiceFactory;
import org.chromium.chrome.browser.crypto_wallet.util.Utils;
@@ -54,6 +55,7 @@ public class BraveEthereumPermissionPromptDialog
static final int MAX_BITMAP_SIZE_FOR_DOWNLOAD = 2048;
private final ModalDialogManager mModalDialogManager;
private int mCoinType;
private final Context mContext;
private long mNativeDialogController;
private PropertyModel mPropertyModel;
@@ -67,23 +69,31 @@ public class BraveEthereumPermissionPromptDialog
private boolean mMojoServicesClosed;
private BraveWalletService mBraveWalletService;
private View mPermissionDialogPositiveButton;
private WalletModel mWalletModel;
@CalledByNative
private static BraveEthereumPermissionPromptDialog create(long nativeDialogController,
@NonNull WindowAndroid windowAndroid, WebContents webContents, String favIconURL) {
@NonNull WindowAndroid windowAndroid, WebContents webContents, String favIconURL,
@CoinType.EnumType int coinType) {
return new BraveEthereumPermissionPromptDialog(
nativeDialogController, windowAndroid, webContents, favIconURL);
nativeDialogController, windowAndroid, webContents, favIconURL, coinType);
}
public BraveEthereumPermissionPromptDialog(long nativeDialogController,
WindowAndroid windowAndroid, WebContents webContents, String favIconURL) {
WindowAndroid windowAndroid, WebContents webContents, String favIconURL,
@CoinType.EnumType int coinType) {
mNativeDialogController = nativeDialogController;
mWebContents = webContents;
mFavIconURL = favIconURL;
mContext = windowAndroid.getActivity().get();
mModalDialogManager = windowAndroid.getModalDialogManager();
mCoinType = coinType;
mMojoServicesClosed = false;
BraveActivity activity = BraveActivity.getBraveActivity();
if (activity != null) {
mWalletModel = activity.getWalletModel();
}
}
@CalledByNative
@@ -114,7 +124,6 @@ public class BraveEthereumPermissionPromptDialog
.build();
mModalDialogManager.showDialog(mPropertyModel, ModalDialogType.APP);
InitKeyringService();
initAccounts();
BraveActivity activity = BraveActivity.getBraveActivity();
if (activity != null) {
activity.dismissWalletPanelOrDialog();
@@ -127,6 +136,7 @@ public class BraveEthereumPermissionPromptDialog
if (mPermissionDialogPositiveButton != null) {
mPermissionDialogPositiveButton.setEnabled(false);
}
initAccounts();
}
@NonNull
@@ -148,29 +158,35 @@ public class BraveEthereumPermissionPromptDialog
mBraveWalletService = BraveWalletServiceFactory.getInstance().getBraveWalletService(this);
}
@SuppressLint("NotifyDataSetChanged")
private void initAccounts() {
assert mKeyringService != null;
mKeyringService.getKeyringInfo(BraveWalletConstants.DEFAULT_KEYRING_ID, keyringInfo -> {
if (keyringInfo == null) {
return;
}
mAccountsListAdapter =
new BraveEthereumPermissionAccountsListAdapter(keyringInfo.accountInfos, true,
new BraveEthereumPermissionAccountsListAdapter
.BraveEthereumPermissionDelegate() {
@Override
public void onAccountCheckChanged(
AccountInfo account, boolean isChecked) {
if (mPermissionDialogPositiveButton != null) {
mPermissionDialogPositiveButton.setEnabled(
getSelectedAccounts().length > 0);
}
}
});
mRecyclerView.setAdapter(mAccountsListAdapter);
LinearLayoutManager layoutManager = new LinearLayoutManager(mContext);
mRecyclerView.setLayoutManager(layoutManager);
});
assert mWalletModel != null;
mAccountsListAdapter = new BraveEthereumPermissionAccountsListAdapter(new AccountInfo[0],
true,
new BraveEthereumPermissionAccountsListAdapter.BraveEthereumPermissionDelegate() {
@Override
public void onAccountCheckChanged(AccountInfo account, boolean isChecked) {
if (mPermissionDialogPositiveButton != null) {
mPermissionDialogPositiveButton.setEnabled(
getSelectedAccounts().length > 0);
}
}
});
mRecyclerView.setAdapter(mAccountsListAdapter);
LinearLayoutManager layoutManager = new LinearLayoutManager(mContext);
mRecyclerView.setLayoutManager(layoutManager);
mWalletModel.getDappsModel().fetchAccountsForConnectionReq(
mCoinType, selectedAccountAllAccounts -> {
String selectedAccount = selectedAccountAllAccounts.first;
List<AccountInfo> accounts = selectedAccountAllAccounts.second;
mAccountsListAdapter.setAccounts(accounts.toArray(new AccountInfo[0]));
if (accounts.size() > 0) {
mAccountsListAdapter.setSelectedAccount(selectedAccount);
mPermissionDialogPositiveButton.setEnabled(true);
}
mAccountsListAdapter.notifyDataSetChanged();
});
}
private void setFavIcon() {
@@ -47,7 +47,7 @@ public class AccountsPermissionsHelper {
accountsPermissionsContexts.add(accountPermissionContext);
mBraveWalletService.hasPermission(
CoinType.ETH, mOrigin, account.address, accountPermissionContext);
account.coin, mOrigin, account.address, accountPermissionContext);
}
accountsPermissionsMultiResponse.setWhenAllCompletedAction(() -> {
@@ -886,11 +886,11 @@ public class Utils {
return defaultValue;
}
public static <T> void removeIf(ArrayList<T> arrayList, Predicate<T> filter) {
public static <T> void removeIf(List<T> list, Predicate<T> filter) {
// Can't use java.util.ArrayList#removeIf with API level 21
ArrayList<Integer> indexesToRemove = new ArrayList<Integer>();
for (int i = 0; i < arrayList.size(); ++i) {
if (filter.test(arrayList.get(i))) {
List<Integer> indexesToRemove = new ArrayList<Integer>();
for (int i = 0; i < list.size(); ++i) {
if (filter.test(list.get(i))) {
indexesToRemove.add(i);
}
}
@@ -899,7 +899,7 @@ public class Utils {
}
Collections.sort(indexesToRemove, Collections.reverseOrder());
for (int i : indexesToRemove) {
arrayList.remove(i);
list.remove(i);
}
}
@@ -33,8 +33,9 @@ GURL GetFavIconURL(const std::vector<blink::mojom::FaviconURLPtr>& candidates) {
BraveEthereumPermissionPromptDialogController::
BraveEthereumPermissionPromptDialogController(
Delegate* delegate,
content::WebContents* web_contents)
: delegate_(delegate), web_contents_(web_contents) {}
content::WebContents* web_contents,
brave_wallet::mojom::CoinType coin_type)
: delegate_(delegate), web_contents_(web_contents), coin_type_(coin_type) {}
BraveEthereumPermissionPromptDialogController::
~BraveEthereumPermissionPromptDialogController() {
@@ -93,5 +94,6 @@ BraveEthereumPermissionPromptDialogController::GetOrCreateJavaObject() {
view_android->GetWindowAndroid()->GetJavaObject(),
web_contents_->GetJavaWebContents(),
base::android::ConvertUTF8ToJavaString(
env, fav_icon_url.is_valid() ? fav_icon_url.spec() : ""));
env, fav_icon_url.is_valid() ? fav_icon_url.spec() : ""),
static_cast<int32_t>(coin_type_));
}
@@ -12,6 +12,7 @@
#include "base/android/scoped_java_ref.h"
#include "base/memory/raw_ptr.h"
#include "brave/components/brave_wallet/common/brave_wallet.mojom.h"
namespace content {
class WebContents;
@@ -29,7 +30,8 @@ class BraveEthereumPermissionPromptDialogController {
// Both the `delegate` and `web_contents` should outlive `this`.
BraveEthereumPermissionPromptDialogController(
Delegate* delegate,
content::WebContents* web_contents_);
content::WebContents* web_contents_,
brave_wallet::mojom::CoinType coin_type_);
~BraveEthereumPermissionPromptDialogController();
void ShowDialog();
@@ -46,6 +48,7 @@ class BraveEthereumPermissionPromptDialogController {
raw_ptr<Delegate> delegate_ = nullptr;
raw_ptr<content::WebContents> web_contents_ = nullptr;
brave_wallet::mojom::CoinType coin_type_;
// The corresponding java object.
base::android::ScopedJavaGlobalRef<jobject> java_object_;
@@ -15,11 +15,12 @@
BraveWalletPermissionPrompt::BraveWalletPermissionPrompt(
content::WebContents* web_contents,
std::unique_ptr<Delegate> delegate)
std::unique_ptr<Delegate> delegate,
brave_wallet::mojom::CoinType coin_type)
: web_contents_(web_contents), delegate_(std::move(delegate)) {
dialog_controller_ =
std::make_unique<BraveEthereumPermissionPromptDialogController>(
this, web_contents_);
this, web_contents_, coin_type);
dialog_controller_->ShowDialog();
}
@@ -12,6 +12,7 @@
#include "base/memory/raw_ptr.h"
#include "brave/browser/permissions/brave_ethereum_permission_prompt_dialog_controller_android.h"
#include "brave/components/brave_wallet/common/brave_wallet.mojom.h"
#include "components/permissions/permissions_client.h"
namespace content {
@@ -39,7 +40,8 @@ class BraveWalletPermissionPrompt
};
BraveWalletPermissionPrompt(content::WebContents* web_contents,
std::unique_ptr<Delegate> delegate);
std::unique_ptr<Delegate> delegate,
brave_wallet::mojom::CoinType coin_type);
~BraveWalletPermissionPrompt() override;
protected:
@@ -53,12 +53,20 @@ ChromePermissionsClient::MaybeCreateMessageUI(
base::WeakPtr<permissions::PermissionPromptAndroid> prompt) {
std::vector<permissions::PermissionRequest*> requests =
prompt->delegate()->Requests();
if (requests.size() != 0 &&
requests[0]->request_type() == permissions::RequestType::kBraveEthereum) {
auto delegate = std::make_unique<BraveWalletPermissionPrompt::Delegate>(
std::move(prompt));
return std::make_unique<BraveWalletPermissionPrompt>(web_contents,
std::move(delegate));
if (requests.size() > 0) {
brave_wallet::mojom::CoinType coin_type =
brave_wallet::mojom::CoinType::ETH;
permissions::RequestType request_type = requests[0]->request_type();
if (request_type == permissions::RequestType::kBraveEthereum ||
request_type == permissions::RequestType::kBraveSolana) {
if (request_type == permissions::RequestType::kBraveSolana) {
coin_type = brave_wallet::mojom::CoinType::SOL;
}
auto delegate = std::make_unique<BraveWalletPermissionPrompt::Delegate>(
std::move(prompt));
return std::make_unique<BraveWalletPermissionPrompt>(
web_contents, std::move(delegate), coin_type);
}
}
return MaybeCreateMessageUI_ChromiumImpl(web_contents, type,