From f6f782fea892517eecc1e6554b7a4904e51367df Mon Sep 17 00:00:00 2001 From: James Brown Date: Mon, 11 Mar 2024 13:46:06 +1100 Subject: [PATCH] Improve TokenIcon (#3365) * Improve consistency of TokenIcons and fix Opensea feed * Remove WC1 code * Add clickable Walletconnect icon to main page for WC sessions --- .github/workflows/lint-pr.yml | 2 +- app/build.gradle | 14 +- app/src/main/AndroidManifest.xml | 5 - app/src/main/java/com/alphawallet/app/C.java | 19 +- .../app/entity/ContractInteract.java | 15 +- .../app/entity/HomeCommsInterface.java | 1 - .../alphawallet/app/entity/HomeReceiver.java | 3 - .../alphawallet/app/entity/ImageEntry.java | 15 + .../alphawallet/app/entity/SuggestEIP1559.kt | 22 +- .../app/entity/nftassets/NFTAsset.java | 11 +- .../app/entity/tokens/ERC1155Token.java | 16 + .../app/entity/tokens/ERC721Token.java | 20 +- .../alphawallet/app/entity/tokens/Token.java | 10 + .../app/interact/WalletConnectInteract.java | 73 - .../SharedPreferenceRepository.java | 2 +- .../app/repository/TokenLocalSource.java | 3 +- .../app/repository/TokenRepository.java | 7 +- .../app/repository/TokenRepositoryType.java | 3 +- .../app/repository/TokensRealmSource.java | 37 +- .../app/service/AssetDefinitionService.java | 50 - .../alphawallet/app/service/GasService.java | 2 +- .../app/service/OpenSeaService.java | 209 +-- .../app/service/TokensService.java | 41 +- .../app/service/WalletConnectService.java | 514 ------ .../app/ui/DappBrowserFragment.java | 5 +- .../com/alphawallet/app/ui/HomeActivity.java | 11 +- .../alphawallet/app/ui/MyAddressActivity.java | 4 +- .../app/ui/NFTAssetDetailActivity.java | 6 +- .../app/ui/QRScanning/QRScannerActivity.java | 16 +- .../alphawallet/app/ui/SearchActivity.java | 2 - .../com/alphawallet/app/ui/SendActivity.java | 24 +- .../app/ui/TransactionDetailActivity.java | 2 +- .../app/ui/WalletConnectActivity.java | 1565 ----------------- .../app/ui/WalletConnectSessionActivity.java | 35 +- .../alphawallet/app/ui/WalletFragment.java | 28 +- .../alphawallet/app/ui/WalletsActivity.java | 5 +- .../app/ui/widget/TokensAdapterCallback.java | 4 +- .../ui/widget/adapter/ActivityAdapter.java | 3 +- .../app/ui/widget/adapter/TokensAdapter.java | 43 +- .../app/ui/widget/adapter/TraitsAdapter.java | 9 +- .../app/ui/widget/entity/IconItem.java | 34 - .../app/ui/widget/holder/EventHolder.java | 2 +- .../ui/widget/holder/SearchTokensHolder.java | 29 +- .../app/ui/widget/holder/TokenGridHolder.java | 2 +- .../app/ui/widget/holder/TokenHolder.java | 2 +- .../app/ui/widget/holder/TokenListHolder.java | 4 +- .../ui/widget/holder/TransactionHolder.java | 6 +- .../app/ui/widget/holder/TransferHolder.java | 2 +- .../java/com/alphawallet/app/util/Utils.java | 2 +- .../app/util/ens/AWEnsResolver.java | 7 +- .../app/viewmodel/DappBrowserViewModel.java | 23 +- .../app/viewmodel/HomeViewModel.java | 90 +- .../app/viewmodel/TokenIconViewModel.java | 171 ++ .../app/viewmodel/WalletConnectViewModel.java | 232 --- .../walletconnect/AWWalletConnectClient.java | 46 +- .../alphawallet/app/walletconnect/WCClient.kt | 274 +-- .../app/walletconnect/entity/WCUtils.java | 52 - .../app/widget/ActionSheetDialog.java | 14 +- .../app/widget/BalanceDisplayWidget.java | 6 +- .../app/widget/EventDetailWidget.java | 4 +- .../alphawallet/app/widget/InputAmount.java | 8 +- .../com/alphawallet/app/widget/TokenIcon.java | 210 ++- .../app/widget/TokenInfoHeaderView.java | 2 +- .../main/res/drawable/ic_wallet_connect.xml | 29 +- .../res/layout/layout_manage_token_search.xml | 99 +- build.sh | 2 +- gradle.properties | 1 - lib/build.gradle | 26 +- .../alphawallet/attestation/Attestation.java | 37 + 69 files changed, 847 insertions(+), 3425 deletions(-) create mode 100644 app/src/main/java/com/alphawallet/app/entity/ImageEntry.java delete mode 100644 app/src/main/java/com/alphawallet/app/service/WalletConnectService.java delete mode 100644 app/src/main/java/com/alphawallet/app/ui/WalletConnectActivity.java create mode 100644 app/src/main/java/com/alphawallet/app/viewmodel/TokenIconViewModel.java delete mode 100644 app/src/main/java/com/alphawallet/app/walletconnect/entity/WCUtils.java diff --git a/.github/workflows/lint-pr.yml b/.github/workflows/lint-pr.yml index ab02a66df9..1b2f235d2a 100644 --- a/.github/workflows/lint-pr.yml +++ b/.github/workflows/lint-pr.yml @@ -27,5 +27,5 @@ jobs: PR_NUMBER: ${{ github.event.number }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - ./gradlew report -PgithubPullRequestId=$PR_NUMBER -PgithubToken=$GITHUB_TOKEN + ./gradlew report --no-configuration-cache -PgithubPullRequestId=$PR_NUMBER -PgithubToken=$GITHUB_TOKEN diff --git a/app/build.gradle b/app/build.gradle index 2be7b1a8f2..770a80e620 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,7 +24,7 @@ tasks.withType(Test).configureEach { } detekt { - toolVersion = "1.20.0-RC1" + toolVersion = "1.23.5" buildUponDefaultConfig = true // preconfigure defaults allRules = false // activate all available (even unstable) rules. baseline = file("$projectDir/check/detekt-baseline.xml") @@ -209,7 +209,7 @@ tasks.register("jacocoAndroidUnitTestReport") { def fileFilter = ['**/R.class', '**/R$*.class', '**/*$ViewInjector*.*', '**/BuildConfig.*', '**/Manifest*.*', '**/*Realm*.*', '**/Generated*.*', '**/*_*.*'] def debugTree = fileTree(dir: "**/", excludes: fileFilter) def mainSrc = "${project.projectDir}/src/main/java" - + sourceDirectories.setFrom(files([mainSrc])) classDirectories.setFrom(files([debugTree])) } @@ -219,9 +219,9 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.aar'], dir: 'libs') - //NB: Downgrade jackson due to bug in 2.15 releases that makes it incompatible with Gradle 8 - //noinspection GradleDependency - implementation platform('com.fasterxml.jackson:jackson-bom:2.13.5') //Don't upgrade from 2.13.5 due to Android API24 compatibility + + //noinspection BomWithoutPlatform,GradleDependency + implementation platform('com.fasterxml.jackson:jackson-bom:2.13.5') //Do not upgrade! 2.13.5 is latest library with Android API24 compatibility implementation 'com.fasterxml.jackson.core:jackson-core' implementation 'com.fasterxml.jackson.core:jackson-databind' @@ -282,8 +282,8 @@ dependencies { //Timber implementation libs.timber - //noinspection UseTomlInstead - implementation platform('com.walletconnect:android-bom:1.13.1') + //noinspection UseTomlInstead,GradleDependency + implementation platform('com.walletconnect:android-bom:1.13.1') //TODO: Upgrade implementation("com.walletconnect:android-core", { exclude group: 'org.web3j', module: '*' }) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7802b1102f..40117de5e5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -27,7 +27,6 @@ - - > getScriptFileURI() return Single.fromCallable(() -> callSmartContractFuncAdaptiveArray(token.tokenInfo.chainId, getScriptURI(), token.getAddress(), token.getWallet())).observeOn(Schedulers.io()); } + public Single getContractURIResult() + { + return Single.fromCallable(() -> callSmartContractFunction(token.tokenInfo.chainId, getContractURI(), token.getAddress(), token.getWallet())) + .map(this::loadMetaData) + .observeOn(Schedulers.io()); + } + private String loadMetaData(String tokenURI) { if (TextUtils.isEmpty(tokenURI)) @@ -65,7 +72,7 @@ public NFTAsset fetchTokenMetadata(BigInteger tokenId) { //1. get TokenURI (check for non-standard URI - check "tokenURI" and "uri") String responseValue = callSmartContractFunction(token.tokenInfo.chainId, getTokenURI(tokenId), token.getAddress(), token.getWallet()); - if (responseValue == null) + if (TextUtils.isEmpty(responseValue)) { responseValue = callSmartContractFunction(token.tokenInfo.chainId, getTokenURI2(tokenId), token.getAddress(), token.getWallet()); } @@ -102,6 +109,12 @@ private static Function getScriptURI() { Collections.singletonList(new TypeReference() {})); } + private static Function getContractURI() { + return new Function("contractURI", + Collections.emptyList(), + Collections.singletonList(new TypeReference() {})); + } + private static void setupClient() { if (client == null) diff --git a/app/src/main/java/com/alphawallet/app/entity/HomeCommsInterface.java b/app/src/main/java/com/alphawallet/app/entity/HomeCommsInterface.java index af9233bf60..75bc98eadb 100644 --- a/app/src/main/java/com/alphawallet/app/entity/HomeCommsInterface.java +++ b/app/src/main/java/com/alphawallet/app/entity/HomeCommsInterface.java @@ -6,5 +6,4 @@ public interface HomeCommsInterface void backupSuccess(String keyAddress); void resetTokens(); void resetTransactions(); - void openWalletConnect(String sessionId); } diff --git a/app/src/main/java/com/alphawallet/app/entity/HomeReceiver.java b/app/src/main/java/com/alphawallet/app/entity/HomeReceiver.java index dc09a55df2..a5f9c74b32 100644 --- a/app/src/main/java/com/alphawallet/app/entity/HomeReceiver.java +++ b/app/src/main/java/com/alphawallet/app/entity/HomeReceiver.java @@ -36,9 +36,6 @@ public void onReceive(Context context, Intent intent) String keyAddress = bundle != null ? bundle.getString("Key", "") : ""; homeCommsInterface.backupSuccess(keyAddress); break; - case C.WALLET_CONNECT_REQUEST: - String sessionId = bundle != null ? bundle.getString("sessionid", "") : ""; - homeCommsInterface.openWalletConnect(sessionId); default: break; } diff --git a/app/src/main/java/com/alphawallet/app/entity/ImageEntry.java b/app/src/main/java/com/alphawallet/app/entity/ImageEntry.java new file mode 100644 index 0000000000..4e82e039b5 --- /dev/null +++ b/app/src/main/java/com/alphawallet/app/entity/ImageEntry.java @@ -0,0 +1,15 @@ +package com.alphawallet.app.entity; + +public class ImageEntry +{ + final public long chainId; + final public String address; + final public String imageUrl; + + public ImageEntry(long networkId, String address, String imageUrl) + { + this.chainId = networkId; + this.address = address; + this.imageUrl = imageUrl; + } +} diff --git a/app/src/main/java/com/alphawallet/app/entity/SuggestEIP1559.kt b/app/src/main/java/com/alphawallet/app/entity/SuggestEIP1559.kt index 1569634f8b..f03d1e145b 100644 --- a/app/src/main/java/com/alphawallet/app/entity/SuggestEIP1559.kt +++ b/app/src/main/java/com/alphawallet/app/entity/SuggestEIP1559.kt @@ -29,7 +29,7 @@ private const val extraPriorityFeeRatio = 0.25 // extra priority fee offere private const val fallbackPriorityFee = 2000000000L // priority fee offered when there are no recent transactions private const val MIN_PRIORITY_FEE = 100000000L // Minimum priority fee in Wei, 0.1 Gwei -fun SuggestEIP1559(gasService: GasService, feeHistory: FeeHistory): Single> { +fun suggestEIP1559(gasService: GasService, feeHistory: FeeHistory): Single> { return suggestPriorityFee(parseLong(feeHistory.oldestBlock.removePrefix("0x"), 16), feeHistory, gasService) .flatMap { priorityFee -> calculateResult(priorityFee, feeHistory) } } @@ -56,22 +56,28 @@ private fun calculateResult(priorityFee: BigInteger, feeHistory: FeeHistory): Si baseFee[baseFee.size - 1] = (baseFee[baseFee.size - 1].toBigDecimal() * BigDecimal(9 / 8.0)).toBigInteger() } - ((feeHistory.gasUsedRatio.size - 1) downTo 0).forEach { i -> + for (i in (feeHistory.gasUsedRatio.size - 1) downTo 0) { if (feeHistory.gasUsedRatio[i] > 0.9) { baseFee[i] = baseFee[i + 1] } } + /*((feeHistory.gasUsedRatio.size - 1) downTo 0).forEach { i -> + if (feeHistory.gasUsedRatio[i] > 0.9) { + baseFee[i] = baseFee[i + 1] + } + }*/ + val order = (0..feeHistory.gasUsedRatio.size).map { it }.sortedBy { baseFee[it] } var maxBaseFee = ZERO val result = mutableMapOf() - (maxTimeFactor downTo 0).forEach { timeFactor -> + for (timeFactor in maxTimeFactor downTo 0) { var bf: BigInteger - if (timeFactor < 1e-6) { - bf = baseFee.last() + bf = if (timeFactor < 1e-6) { + baseFee.last() } else { - bf = predictMinBaseFee(baseFee, order, timeFactor.toDouble(), consistentBaseFee) + predictMinBaseFee(baseFee, order, timeFactor.toDouble(), consistentBaseFee) } var t = BigDecimal(usePriorityFee) if (bf > maxBaseFee) { @@ -145,8 +151,8 @@ internal fun suggestPriorityFee(firstBlock: Long, feeHistory: FeeHistory, gasSer rewardPercentile.toString()).blockingGet(); val rewardSize = feeHistoryFetch?.reward?.size ?: 0 - (0 until rewardSize).forEach { - rewards.add(BigInteger(Numeric.cleanHexPrefix(feeHistoryFetch.reward[it][0].removePrefix("0x")), + for (index in 0 until rewardSize) { + rewards.add(BigInteger(Numeric.cleanHexPrefix(feeHistoryFetch.reward[index][0].removePrefix("0x")), 16)) } if (rewardSize < blockCount) break diff --git a/app/src/main/java/com/alphawallet/app/entity/nftassets/NFTAsset.java b/app/src/main/java/com/alphawallet/app/entity/nftassets/NFTAsset.java index 521743226f..c608bb7497 100644 --- a/app/src/main/java/com/alphawallet/app/entity/nftassets/NFTAsset.java +++ b/app/src/main/java/com/alphawallet/app/entity/nftassets/NFTAsset.java @@ -52,11 +52,13 @@ public NFTAsset[] newArray(int size) }; private static final String LOADING_TOKEN = "*Loading*"; private static final String ID = "id"; + private static final String OPENSEA_ID = "identifier"; private static final String ATTN_ID = "attn_id"; private static final String NAME = "name"; private static final String IMAGE = "image"; private static final String IMAGE_URL = "image_url"; private static final String IMAGE_PREVIEW = "image_preview_url"; + private static final String COLLECTION = "collection"; private static final String DESCRIPTION = "description"; private static final String IMAGE_ORIGINAL_URL = "image_original_url"; private static final String IMAGE_ANIMATION = "animation_url"; @@ -66,7 +68,7 @@ public NFTAsset[] newArray(int size) private static final String[] IMAGE_THUMBNAIL_DESIGNATORS = {IMAGE_PREVIEW, IMAGE, IMAGE_URL, IMAGE_ORIGINAL_URL, IMAGE_ANIMATION}; private static final String BACKGROUND_COLOUR = "background_color"; private static final String EXTERNAL_LINK = "external_link"; - private static final List DESIRED_PARAMS = Arrays.asList(NAME, BACKGROUND_COLOUR, IMAGE_URL, IMAGE, IMAGE_ORIGINAL_URL, IMAGE_PREVIEW, DESCRIPTION, EXTERNAL_LINK, IMAGE_ANIMATION); + private static final List DESIRED_PARAMS = Arrays.asList(NAME, BACKGROUND_COLOUR, IMAGE_URL, IMAGE, IMAGE_ORIGINAL_URL, IMAGE_PREVIEW, DESCRIPTION, EXTERNAL_LINK, IMAGE_ANIMATION, COLLECTION); private static final List ATTRIBUTE_DESCRIPTOR = Arrays.asList("attributes", "traits"); private final Map assetMap = new HashMap<>(); private final Map attributeMap = new HashMap<>(); @@ -265,6 +267,11 @@ private void loadFromMetaData(String metaData) try { JSONObject jsonData = new JSONObject(metaData); + if (jsonData.has("nft")) + { + //need to unwrap this return value + jsonData = jsonData.getJSONObject("nft"); + } Iterator keys = jsonData.keys(); String id = null; @@ -272,7 +279,7 @@ private void loadFromMetaData(String metaData) { String key = keys.next(); String value = jsonData.getString(key); - if (key.equals(ID)) + if (key.equals(ID) || key.equals(OPENSEA_ID)) { id = value; } diff --git a/app/src/main/java/com/alphawallet/app/entity/tokens/ERC1155Token.java b/app/src/main/java/com/alphawallet/app/entity/tokens/ERC1155Token.java index aafc723bed..eb6c6a18db 100644 --- a/app/src/main/java/com/alphawallet/app/entity/tokens/ERC1155Token.java +++ b/app/src/main/java/com/alphawallet/app/entity/tokens/ERC1155Token.java @@ -814,6 +814,22 @@ && getNFTTokenId(tokenId).compareTo(BigInteger.valueOf(0xFFFF)) < 0 && getNFTTokenId(tokenId).compareTo(BigInteger.ZERO) > 0; } + @Override + public String getFirstImageUrl() + { + if (assets != null && !assets.isEmpty() && assets.values().stream().findFirst().isPresent()) + { + //get first asset + NFTAsset firstAsset = assets.values().stream().findFirst().get(); + if (firstAsset.hasImageAsset()) + { + return firstAsset.getThumbnail(); + } + } + + return ""; + } + @Override public boolean isBatchTransferAvailable() { diff --git a/app/src/main/java/com/alphawallet/app/entity/tokens/ERC721Token.java b/app/src/main/java/com/alphawallet/app/entity/tokens/ERC721Token.java index 1276c54836..24055cbcf0 100644 --- a/app/src/main/java/com/alphawallet/app/entity/tokens/ERC721Token.java +++ b/app/src/main/java/com/alphawallet/app/entity/tokens/ERC721Token.java @@ -427,7 +427,7 @@ private void updateEnumerableBalance(Web3j web3j, Realm realm) throws IOExceptio { // find tokenId from index String tokenId = callSmartContractFunction(tokenInfo.chainId, tokenOfOwnerByIndex(BigInteger.valueOf(tokenIndex)), getAddress(), getWallet()); - if (tokenId == null) continue; + if (TextUtils.isEmpty(tokenId)) continue; tokenIdsHeld.add(new BigInteger(tokenId)); } } @@ -620,7 +620,7 @@ private HashSet checkBalances(Web3j web3j, HashSet event for (BigInteger tokenId : eventIds) { String owner = callSmartContractFunction(tokenInfo.chainId, ownerOf(tokenId), getAddress(), getWallet()); - if (owner == null || owner.equalsIgnoreCase(getWallet())) + if (TextUtils.isEmpty(owner) || owner.equalsIgnoreCase(getWallet())) { heldTokens.add(tokenId); } @@ -728,6 +728,22 @@ public EthFilter getSendBalanceFilter(Event event, DefaultBlockParameter startBl return filter; } + @Override + public String getFirstImageUrl() + { + if (tokenBalanceAssets != null && !tokenBalanceAssets.isEmpty() && tokenBalanceAssets.values().stream().findFirst().isPresent()) + { + //get first asset + NFTAsset firstAsset = tokenBalanceAssets.values().stream().findFirst().get(); + if (firstAsset.hasImageAsset()) + { + return firstAsset.getThumbnail(); + } + } + + return ""; + } + public String getTransferID(Transaction tx) { if (tx.transactionInput != null && tx.transactionInput.miscData.size() > 0) diff --git a/app/src/main/java/com/alphawallet/app/entity/tokens/Token.java b/app/src/main/java/com/alphawallet/app/entity/tokens/Token.java index 7033d5f4db..7b34cc55bd 100644 --- a/app/src/main/java/com/alphawallet/app/entity/tokens/Token.java +++ b/app/src/main/java/com/alphawallet/app/entity/tokens/Token.java @@ -1083,6 +1083,11 @@ public Single> getScriptURI() return contractInteract.getScriptFileURI(); } + public Single getContractURI() + { + return contractInteract.getContractURIResult(); + } + /** * Event filters for send and receive of the token, overriden by the token type */ @@ -1153,4 +1158,9 @@ public String getAttestationCollectionId(TokenDefinition td) { return getTSKey(); } + + public String getFirstImageUrl() + { + return ""; + } } diff --git a/app/src/main/java/com/alphawallet/app/interact/WalletConnectInteract.java b/app/src/main/java/com/alphawallet/app/interact/WalletConnectInteract.java index 81a88f256c..55e75aae6c 100644 --- a/app/src/main/java/com/alphawallet/app/interact/WalletConnectInteract.java +++ b/app/src/main/java/com/alphawallet/app/interact/WalletConnectInteract.java @@ -1,33 +1,16 @@ package com.alphawallet.app.interact; -import android.content.ComponentName; -import android.content.Context; -import android.content.ServiceConnection; -import android.os.IBinder; - -import com.alphawallet.app.entity.WalletConnectActions; -import com.alphawallet.app.entity.lifi.Token; import com.alphawallet.app.entity.walletconnect.WalletConnectSessionItem; import com.alphawallet.app.entity.walletconnect.WalletConnectV2SessionItem; -import com.alphawallet.app.repository.entity.RealmWCSession; import com.alphawallet.app.service.RealmManager; -import com.alphawallet.app.service.WalletConnectService; -import com.alphawallet.app.viewmodel.WalletConnectViewModel; -import com.alphawallet.app.walletconnect.WCClient; -import com.alphawallet.app.walletconnect.entity.WCUtils; import com.walletconnect.web3.wallet.client.Wallet; import com.walletconnect.web3.wallet.client.Web3Wallet; -import java.math.BigDecimal; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import javax.inject.Inject; -import io.realm.Realm; -import io.realm.RealmResults; -import io.realm.Sort; import timber.log.Timber; public class WalletConnectInteract @@ -49,7 +32,6 @@ public List getSessions() { List result = new ArrayList<>(); result.addAll(getWalletConnectV2SessionItems()); - result.addAll(getWalletConnectV1SessionItems()); //now sort for active/newness result.sort((l, r) -> Long.compare(r.expiryTime, l.expiryTime)); @@ -57,61 +39,6 @@ public List getSessions() return result; } - public void fetchSessions(Context context, SessionFetchCallback sessionFetchCallback) - { - ServiceConnection connection = new ServiceConnection() - { - @Override - public void onServiceConnected(ComponentName name, IBinder service) - { - WalletConnectService walletConnectService = ((WalletConnectService.LocalBinder) service).getService(); - fetch(walletConnectService, sessionFetchCallback); - } - - @Override - public void onServiceDisconnected(ComponentName name) - { - } - }; - - WCUtils.startServiceLocal(context, connection, WalletConnectActions.CONNECT); - } - - private void fetch(WalletConnectService walletConnectService, SessionFetchCallback sessionFetchCallback) - { - List result = new ArrayList<>(); - List sessionItems = getWalletConnectV1SessionItems(); - for (WalletConnectSessionItem item : sessionItems) - { - WCClient wcClient = walletConnectService.getClient(item.sessionId); - if (wcClient != null && wcClient.isConnected()) - { - result.add(item); - } - } - - result.addAll(getWalletConnectV2SessionItems()); - sessionFetchCallback.onFetched(result); - } - - private List getWalletConnectV1SessionItems() - { - List sessions = new ArrayList<>(); - try (Realm realm = realmManager.getRealmInstance(WalletConnectViewModel.WC_SESSION_DB)) - { - RealmResults items = realm.where(RealmWCSession.class) - .sort("lastUsageTime", Sort.DESCENDING) - .findAll(); - - for (RealmWCSession r : items) - { - sessions.add(new WalletConnectSessionItem(r)); - } - } - - return sessions; - } - private List getWalletConnectV2SessionItems() { List result = new ArrayList<>(); diff --git a/app/src/main/java/com/alphawallet/app/repository/SharedPreferenceRepository.java b/app/src/main/java/com/alphawallet/app/repository/SharedPreferenceRepository.java index fc4e1d7dba..5ace4ee3f2 100644 --- a/app/src/main/java/com/alphawallet/app/repository/SharedPreferenceRepository.java +++ b/app/src/main/java/com/alphawallet/app/repository/SharedPreferenceRepository.java @@ -478,7 +478,7 @@ public void setPostNotificationsPermissionRequested(String address, boolean hasR @Override public boolean getUseTSViewer() { - return pref.getBoolean(USE_TOKENSCRIPT_VIEWER, true); + return pref.getBoolean(USE_TOKENSCRIPT_VIEWER, false); } @Override diff --git a/app/src/main/java/com/alphawallet/app/repository/TokenLocalSource.java b/app/src/main/java/com/alphawallet/app/repository/TokenLocalSource.java index cb0f8e3010..8f8abe78b8 100644 --- a/app/src/main/java/com/alphawallet/app/repository/TokenLocalSource.java +++ b/app/src/main/java/com/alphawallet/app/repository/TokenLocalSource.java @@ -3,6 +3,7 @@ import android.util.Pair; import com.alphawallet.app.entity.ContractType; +import com.alphawallet.app.entity.ImageEntry; import com.alphawallet.app.entity.Wallet; import com.alphawallet.app.entity.nftassets.NFTAsset; import com.alphawallet.app.entity.tokendata.TokenGroup; @@ -38,7 +39,7 @@ public interface TokenLocalSource void deleteRealmTokens(Wallet wallet, List tcmList); - void storeTokenUrl(long chainId, String address, String imageUrl); + void storeTokenUrl(List entries); Token initNFTAssets(Wallet wallet, Token tokens); diff --git a/app/src/main/java/com/alphawallet/app/repository/TokenRepository.java b/app/src/main/java/com/alphawallet/app/repository/TokenRepository.java index 01c79b1372..af4c1a1041 100644 --- a/app/src/main/java/com/alphawallet/app/repository/TokenRepository.java +++ b/app/src/main/java/com/alphawallet/app/repository/TokenRepository.java @@ -16,6 +16,7 @@ import com.alphawallet.app.C; import com.alphawallet.app.entity.ContractLocator; import com.alphawallet.app.entity.ContractType; +import com.alphawallet.app.entity.ImageEntry; import com.alphawallet.app.entity.NetworkInfo; import com.alphawallet.app.entity.TransferFromEventResponse; import com.alphawallet.app.entity.Wallet; @@ -1295,9 +1296,9 @@ public Single fetchIsRedeemed(Token token, BigInteger tokenId) } @Override - public void addImageUrl(long networkId, String address, String imageUrl) + public void addImageUrl(List entries) { - localSource.storeTokenUrl(networkId, address, imageUrl); + localSource.storeTokenUrl(entries); } public static Web3j getWeb3jServiceForEvents(long chainId) @@ -1358,7 +1359,7 @@ public static String callSmartContractFunction(long chainId, // } - return null; + return ""; } public static List callSmartContractFuncAdaptiveArray(long chainId, diff --git a/app/src/main/java/com/alphawallet/app/repository/TokenRepositoryType.java b/app/src/main/java/com/alphawallet/app/repository/TokenRepositoryType.java index fd232f4f87..5556f7451e 100644 --- a/app/src/main/java/com/alphawallet/app/repository/TokenRepositoryType.java +++ b/app/src/main/java/com/alphawallet/app/repository/TokenRepositoryType.java @@ -4,6 +4,7 @@ import com.alphawallet.app.entity.ContractLocator; import com.alphawallet.app.entity.ContractType; +import com.alphawallet.app.entity.ImageEntry; import com.alphawallet.app.entity.TransferFromEventResponse; import com.alphawallet.app.entity.Wallet; import com.alphawallet.app.entity.nftassets.NFTAsset; @@ -66,7 +67,7 @@ public interface TokenRepositoryType Single fetchIsRedeemed(Token token, BigInteger tokenId); - void addImageUrl(long chainId, String address, String imageUrl); + void addImageUrl(List entries); void updateLocalAddress(String walletAddress); diff --git a/app/src/main/java/com/alphawallet/app/repository/TokensRealmSource.java b/app/src/main/java/com/alphawallet/app/repository/TokensRealmSource.java index 45d29a0aaf..ea6142ee42 100644 --- a/app/src/main/java/com/alphawallet/app/repository/TokensRealmSource.java +++ b/app/src/main/java/com/alphawallet/app/repository/TokensRealmSource.java @@ -9,6 +9,7 @@ import com.alphawallet.app.entity.ContractType; import com.alphawallet.app.entity.CustomViewSettings; +import com.alphawallet.app.entity.ImageEntry; import com.alphawallet.app.entity.NetworkInfo; import com.alphawallet.app.entity.Wallet; import com.alphawallet.app.entity.nftassets.NFTAsset; @@ -717,31 +718,29 @@ public Single storeTokenInfo(Wallet wallet, TokenInfo tInfo, Contract } @Override - public void storeTokenUrl(long networkId, String address, String imageUrl) + public void storeTokenUrl(List entries) { try (Realm realm = realmManager.getRealmInstance(IMAGES_DB)) { - final String instanceKey = address.toLowerCase() + "-" + networkId; - final RealmAuxData instance = realm.where(RealmAuxData.class).equalTo("instanceKey", instanceKey).findFirst(); + realm.executeTransaction(r -> { + for (ImageEntry thisEntry : entries) + { + final String instanceKey = thisEntry.address.toLowerCase() + "-" + thisEntry.chainId; + RealmAuxData instance = r.where(RealmAuxData.class).equalTo("instanceKey", instanceKey).findFirst(); - if (instance == null || !instance.getResult().equals(imageUrl)) - { - realm.executeTransactionAsync(r -> { - RealmAuxData aux; - if (instance == null) + if (instance == null || !instance.getResult().equals(thisEntry.imageUrl)) { - aux = r.createObject(RealmAuxData.class, instanceKey); - } - else - { - aux = instance; - } + if (instance == null) + { + instance = r.createObject(RealmAuxData.class, instanceKey); + } - aux.setResult(imageUrl); - aux.setResultTime(System.currentTimeMillis()); - r.insertOrUpdate(aux); - }); - } + instance.setResult(thisEntry.imageUrl); + instance.setResultTime(System.currentTimeMillis()); + r.insertOrUpdate(instance); + } + } + }); } } diff --git a/app/src/main/java/com/alphawallet/app/service/AssetDefinitionService.java b/app/src/main/java/com/alphawallet/app/service/AssetDefinitionService.java index 2739528a7d..56e19b4aa2 100644 --- a/app/src/main/java/com/alphawallet/app/service/AssetDefinitionService.java +++ b/app/src/main/java/com/alphawallet/app/service/AssetDefinitionService.java @@ -3148,56 +3148,6 @@ public void storeTokenViewHeight(long chainId, String address, int listViewHeigh } } - public String getTokenImageUrl(long networkId, String address) - { - String url = ""; - String instanceKey = address.toLowerCase() + "-" + networkId; - try (Realm realm = realmManager.getRealmInstance(IMAGES_DB)) - { - RealmAuxData instance = realm.where(RealmAuxData.class) - .equalTo("instanceKey", instanceKey) - .findFirst(); - - if (instance != null) - { - url = instance.getResult(); - } - } - catch (Exception ex) - { - Timber.e(ex); - } - - return url; - } - - public Pair getFallbackUrlForToken(Token token) - { - boolean storedOverride = false; - String correctedAddr = Keys.toChecksumAddress(token.getAddress()); - - String tURL = getTokenImageUrl(token.tokenInfo.chainId, token.getAddress()); - if (TextUtils.isEmpty(tURL)) - { - tURL = Utils.getTWTokenImageUrl(token.tokenInfo.chainId, correctedAddr); - } - else - { - storedOverride = true; - } - - return new Pair<>(tURL, storedOverride); - } - - public void storeImageUrl(long chainId, String imageUrl) - { - String tokenAddress = Utils.getTokenAddrFromAWUrl(imageUrl); - if (!TextUtils.isEmpty(tokenAddress)) - { - tokensService.addTokenImageUrl(chainId, tokenAddress, imageUrl); - } - } - public Single fetchViewHeight(long chainId, String address) { return Single.fromCallable(() -> { diff --git a/app/src/main/java/com/alphawallet/app/service/GasService.java b/app/src/main/java/com/alphawallet/app/service/GasService.java index 3ffd5c79f4..db891d9ab7 100644 --- a/app/src/main/java/com/alphawallet/app/service/GasService.java +++ b/app/src/main/java/com/alphawallet/app/service/GasService.java @@ -480,7 +480,7 @@ private Single> useCalculationIfRequired(Ma private Single> getEIP1559FeeStructureCalculation() { return getChainFeeHistory(100, "latest", "") - .flatMap(feeHistory -> SuggestEIP1559Kt.SuggestEIP1559(this, feeHistory)); + .flatMap(feeHistory -> SuggestEIP1559Kt.suggestEIP1559(this, feeHistory)); } private void handleError(Throwable err) diff --git a/app/src/main/java/com/alphawallet/app/service/OpenSeaService.java b/app/src/main/java/com/alphawallet/app/service/OpenSeaService.java index 906b42287f..04792d2849 100644 --- a/app/src/main/java/com/alphawallet/app/service/OpenSeaService.java +++ b/app/src/main/java/com/alphawallet/app/service/OpenSeaService.java @@ -1,5 +1,15 @@ package com.alphawallet.app.service; +import static com.alphawallet.ethereum.EthereumNetworkBase.ARBITRUM_MAIN_ID; +import static com.alphawallet.ethereum.EthereumNetworkBase.AVALANCHE_ID; +import static com.alphawallet.ethereum.EthereumNetworkBase.BINANCE_MAIN_ID; +import static com.alphawallet.ethereum.EthereumNetworkBase.KLAYTN_ID; +import static com.alphawallet.ethereum.EthereumNetworkBase.MAINNET_ID; +import static com.alphawallet.ethereum.EthereumNetworkBase.OPTIMISTIC_MAIN_ID; +import static com.alphawallet.ethereum.EthereumNetworkBase.POLYGON_ID; +import static com.alphawallet.ethereum.EthereumNetworkBase.POLYGON_TEST_ID; +import static com.alphawallet.ethereum.EthereumNetworkBase.SEPOLIA_TESTNET_ID; + import android.net.Uri; import android.text.TextUtils; import android.text.format.DateUtils; @@ -23,7 +33,9 @@ import java.math.BigInteger; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import io.reactivex.Single; @@ -40,11 +52,23 @@ public class OpenSeaService { private final OkHttpClient httpClient; - private static final int PAGE_SIZE = 50; + private static final int PAGE_SIZE = 200; private final Map imageUrls = new HashMap<>(); private static final TokenFactory tf = new TokenFactory(); private final LongSparseArray networkCheckTimes = new LongSparseArray<>(); - private final LongSparseArray pageOffsets = new LongSparseArray<>(); + private final Map pageOffsets = new ConcurrentHashMap<>(); + + private final Map API_CHAIN_MAP = Map.of( + MAINNET_ID, "ethereum", + KLAYTN_ID, "klaytn", + POLYGON_TEST_ID, "mumbai", + POLYGON_ID, "matic", + OPTIMISTIC_MAIN_ID, "optimism", + ARBITRUM_MAIN_ID, "arbitrum", + SEPOLIA_TESTNET_ID, "sepolia", + AVALANCHE_ID, "avalanche", + BINANCE_MAIN_ID, "bsc" + ); public OpenSeaService() { @@ -115,46 +139,42 @@ public Single getTokens(String address, if (!canCheckChain(networkId)) return new Token[0]; networkCheckTimes.put(networkId, currentTime); - int pageOffset = pageOffsets.get(networkId, 0); + String pageCursor = pageOffsets.getOrDefault(networkId, ""); Timber.d("Fetch from opensea : %s", networkName); do { - String jsonData = fetchAssets(networkId, address, pageOffset); + String jsonData = fetchAssets(networkId, address, pageCursor); if (!JsonUtils.hasAssets(jsonData)) { return foundTokens.values().toArray(new Token[0]); //on error return results found so far } JSONObject result = new JSONObject(jsonData); - JSONArray assets; - if (result.has("assets")) - { - assets = result.getJSONArray("assets"); - } - else - { - assets = result.getJSONArray("results"); - } + JSONArray assets = result.getJSONArray("nfts"); receivedTokens = assets.length(); - pageOffset += assets.length(); //process this page of results processOpenseaTokens(foundTokens, assets, address, networkId, networkName, tokensService); currentPage++; + pageCursor = result.getString("next"); + if (TextUtils.isEmpty(pageCursor)) + { + break; + } } - while (receivedTokens == PAGE_SIZE && currentPage <= 3); //fetch 4 pages for each loop + while (currentPage <= 3); //fetch 4 pages for each loop + + pageOffsets.put(networkId, pageCursor); if (receivedTokens < PAGE_SIZE) { - Timber.d("Reset OpenSeaAPI reads at: %s", pageOffset); - pageOffsets.put(networkId, 0); + Timber.d("Reset OpenSeaAPI reads at: %s", pageCursor); } else { - pageOffsets.put(networkId, pageOffset); networkCheckTimes.put(networkId, currentTime - 55 * DateUtils.SECOND_IN_MILLIS); //do another read within 5 seconds } @@ -181,19 +201,18 @@ private void processOpenseaTokens(Map foundTokens, for (int i = 0; i < assets.length(); i++) { JSONObject assetJSON = assets.getJSONObject(i); - AssetContract assetContract = - new Gson().fromJson(assetJSON.getString("asset_contract"), AssetContract.class); + String tokenStandard = assetJSON.getString("token_standard").toLowerCase(Locale.ROOT); - if (assetContract != null && !TextUtils.isEmpty(assetContract.getSchemaName())) + if (!TextUtils.isEmpty(tokenStandard)) { - switch (assetContract.getSchemaName()) + switch (tokenStandard) { - case "ERC721": - handleERC721(assetContract, assetList, assetJSON, networkId, foundTokens, tokensService, + case "erc721": + handleERC721(assetList, assetJSON, networkId, foundTokens, tokensService, networkName, address); break; - case "ERC1155": - handleERC1155(assetContract, assetList, assetJSON, networkId, foundTokens, tokensService, + case "erc1155": + handleERC1155(assetList, assetJSON, networkId, foundTokens, tokensService, networkName, address); break; } @@ -201,8 +220,7 @@ private void processOpenseaTokens(Map foundTokens, } } - private void handleERC721(AssetContract assetContract, - Map> assetList, + private void handleERC721(Map> assetList, JSONObject assetJSON, long networkId, Map foundTokens, @@ -212,52 +230,50 @@ private void handleERC721(AssetContract assetContract, { NFTAsset asset = new NFTAsset(assetJSON.toString()); - BigInteger tokenId = assetJSON.has("token_id") ? - new BigInteger(assetJSON.getString("token_id")) + BigInteger tokenId = assetJSON.has("identifier") ? + new BigInteger(assetJSON.getString("identifier")) : null; if (tokenId == null) return; - addAssetImageToHashMap(assetContract.getAddress(), assetContract.getImageUrl()); + String contractAddress = assetJSON.getString("contract"); + String collectionName = assetJSON.getString("collection"); - Token token = foundTokens.get(assetContract.getAddress()); + Token token = foundTokens.get(contractAddress); if (token == null) { TokenInfo tInfo; ContractType type; long lastCheckTime = 0; - Token checkToken = svs.getToken(networkId, assetContract.getAddress()); + Token checkToken = svs.getToken(networkId, contractAddress); if (checkToken != null && (checkToken.isERC721() || checkToken.isERC721Ticket())) { - assetList.put(assetContract.getAddress(), checkToken.getTokenAssets()); + assetList.put(contractAddress, checkToken.getTokenAssets()); tInfo = checkToken.tokenInfo; type = checkToken.getInterfaceSpec(); lastCheckTime = checkToken.lastTxTime; - JSONObject collectionJSON = assetJSON.getJSONObject("collection"); - String collectionName = collectionJSON.getString("name"); - if (!TextUtils.isEmpty(collectionName) && (TextUtils.isEmpty(checkToken.tokenInfo.name) || !collectionName.equals(checkToken.tokenInfo.name))) + if (!TextUtils.isEmpty(collectionName) && (TextUtils.isEmpty(checkToken.tokenInfo.name))) { //Update to collection name if the token name is blank, or if the collection name is not blank and current token name is different - tInfo = new TokenInfo(assetContract.getAddress(), collectionName, assetContract.getSymbol(), 0, tInfo.isEnabled, networkId); + tInfo = new TokenInfo(contractAddress, collectionName, "", 0, tInfo.isEnabled, networkId); } } else //if we haven't seen the contract before, or it was previously logged as something other than a ERC721 variant then specify undetermined flag { - tInfo = new TokenInfo(assetContract.getAddress(), assetContract.getName(), assetContract.getSymbol(), 0, true, networkId); - type = ContractType.ERC721_UNDETERMINED; + tInfo = new TokenInfo(contractAddress, asset.getName(), "", 0, true, networkId); + type = ContractType.ERC721; } token = tf.createToken(tInfo, type, networkName); token.setTokenWallet(address); token.lastTxTime = lastCheckTime; - foundTokens.put(assetContract.getAddress(), token); + foundTokens.put(contractAddress, token); } asset.updateAsset(tokenId, assetList.get(token.getAddress())); token.addAssetToTokenBalanceAssets(tokenId, asset); } - private void handleERC1155(AssetContract assetContract, - Map> assetList, + private void handleERC1155(Map> assetList, JSONObject assetJSON, long networkId, Map foundTokens, @@ -267,39 +283,39 @@ private void handleERC1155(AssetContract assetContract, { NFTAsset asset = new NFTAsset(assetJSON.toString()); - BigInteger tokenId = assetJSON.has("token_id") ? - new BigInteger(assetJSON.getString("token_id")) + BigInteger tokenId = assetJSON.has("identifier") ? + new BigInteger(assetJSON.getString("identifier")) : null; if (tokenId == null) return; - addAssetImageToHashMap(assetContract.getAddress(), assetContract.getImageUrl()); + String contractAddress = assetJSON.getString("contract"); + String collectionName = assetJSON.getString("collection"); - Token token = foundTokens.get(assetContract.getAddress()); + Token token = foundTokens.get(contractAddress); if (token == null) { TokenInfo tInfo; ContractType type; long lastCheckTime = 0; - Token checkToken = svs.getToken(networkId, assetContract.getAddress()); + Token checkToken = svs.getToken(networkId, contractAddress); if (checkToken != null && checkToken.getInterfaceSpec() == ContractType.ERC1155) { - assetList.put(assetContract.getAddress(), checkToken.getTokenAssets()); + assetList.put(contractAddress, checkToken.getTokenAssets()); tInfo = checkToken.tokenInfo; type = checkToken.getInterfaceSpec(); lastCheckTime = checkToken.lastTxTime; } else { - tInfo = new TokenInfo(assetContract.getAddress(), assetContract.getName(), assetContract.getSymbol(), 0, true, networkId); + tInfo = new TokenInfo(contractAddress, collectionName, "", 0, true, networkId); type = ContractType.ERC1155; } token = tf.createToken(tInfo, type, networkName); token.setTokenWallet(address); token.lastTxTime = lastCheckTime; - token.setAssetContract(assetContract); - foundTokens.put(assetContract.getAddress(), token); + foundTokens.put(contractAddress, token); } asset.updateAsset(tokenId, assetList.get(token.getAddress())); token.addAssetToTokenBalanceAssets(tokenId, asset); @@ -352,92 +368,37 @@ public Single getCollection(Token token, String slug) fetchCollection(token.tokenInfo.chainId, slug)); } - public String fetchAssets(long networkId, String address, int offset) + public String fetchAssets(long networkId, String address, String pageCursor) { - String api = ""; - String ownerOption = "owner"; - - //TODO: Put these into a mapping - if (networkId == EthereumNetworkBase.MAINNET_ID) - { - api = C.OPENSEA_ASSETS_API_MAINNET; - } - else if (networkId == EthereumNetworkBase.GOERLI_ID) - { - api = C.OPENSEA_ASSETS_API_TESTNET; - } - else if (networkId == EthereumNetworkBase.POLYGON_ID) - { - api = C.OPENSEA_ASSETS_API_MATIC; - ownerOption = "owner_address"; - } - else if (networkId == EthereumNetworkBase.ARBITRUM_MAIN_ID) - { - api = C.OPENSEA_ASSETS_API_ARBITRUM; - ownerOption = "owner_address"; - } - else if (networkId == EthereumNetworkBase.AVALANCHE_ID) - { - api = C.OPENSEA_ASSETS_API_AVALANCHE; - ownerOption = "owner_address"; - } - else if (networkId == EthereumNetworkBase.KLAYTN_ID) + String mappingName = API_CHAIN_MAP.get(networkId); + if (TextUtils.isEmpty(mappingName)) { - api = C.OPENSEA_ASSETS_API_KLAYTN; - ownerOption = "owner_address"; - } - else if (networkId == EthereumNetworkBase.OPTIMISTIC_MAIN_ID) - { - api = C.OPENSEA_ASSETS_API_OPTIMISM; - ownerOption = "owner_address"; + return JsonUtils.EMPTY_RESULT; } - if (!TextUtils.isEmpty(api)) - { - Uri.Builder builder = new Uri.Builder(); - builder.encodedPath(api) - .appendQueryParameter(ownerOption, address) - .appendQueryParameter("limit", String.valueOf(PAGE_SIZE)) - .appendQueryParameter("offset", String.valueOf(offset)); + String api = C.OPENSEA_ASSETS_API_V2.replace("{CHAIN}", mappingName).replace("{ADDRESS}", address); - return executeRequest(networkId, builder.build().toString()); + Uri.Builder builder = new Uri.Builder(); + builder.encodedPath(api) + .appendQueryParameter("limit", String.valueOf(PAGE_SIZE)); + + if (!TextUtils.isEmpty(pageCursor)) + { + builder.appendQueryParameter("next", pageCursor); } - return JsonUtils.EMPTY_RESULT; + return executeRequest(networkId, builder.build().toString()); } public String fetchAsset(long networkId, String contractAddress, String tokenId) { - String api = ""; - if (networkId == EthereumNetworkBase.MAINNET_ID) - { - api = C.OPENSEA_SINGLE_ASSET_API_MAINNET + contractAddress + "/" + tokenId; - } - else if (networkId == EthereumNetworkBase.GOERLI_ID) - { - api = C.OPENSEA_SINGLE_ASSET_API_TESTNET + contractAddress + "/" + tokenId; - } - else if (networkId == EthereumNetworkBase.POLYGON_ID) - { - api = C.OPENSEA_SINGLE_ASSET_API_MATIC + contractAddress + "/" + tokenId; - } - else if (networkId == EthereumNetworkBase.ARBITRUM_MAIN_ID) - { - api = C.OPENSEA_SINGLE_ASSET_API_ARBITRUM + contractAddress + "/" + tokenId; - } - else if (networkId == EthereumNetworkBase.AVALANCHE_ID) - { - api = C.OPENSEA_SINGLE_ASSET_API_AVALANCHE + contractAddress + "/" + tokenId; - } - else if (networkId == EthereumNetworkBase.KLAYTN_ID) - { - api = C.OPENSEA_SINGLE_ASSET_API_KLAYTN + contractAddress + "/" + tokenId; - } - else if (networkId == EthereumNetworkBase.OPTIMISTIC_MAIN_ID) + String mappingName = API_CHAIN_MAP.get(networkId); + if (TextUtils.isEmpty(mappingName)) { - api = C.OPENSEA_SINGLE_ASSET_API_OPTIMISM + contractAddress + "/" + tokenId; + return JsonUtils.EMPTY_RESULT; } + String api = C.OPENSEA_NFT_API_V2.replace("{CHAIN}", mappingName).replace("{ADDRESS}", contractAddress).replace("{TOKEN_ID}", tokenId); return executeRequest(networkId, api); } diff --git a/app/src/main/java/com/alphawallet/app/service/TokensService.java b/app/src/main/java/com/alphawallet/app/service/TokensService.java index d85c9a370f..c4e9ac95d5 100644 --- a/app/src/main/java/com/alphawallet/app/service/TokensService.java +++ b/app/src/main/java/com/alphawallet/app/service/TokensService.java @@ -3,6 +3,7 @@ import static com.alphawallet.app.repository.TokensRealmSource.databaseKey; import static com.alphawallet.ethereum.EthereumNetworkBase.MAINNET_ID; +import android.media.Image; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Pair; @@ -15,6 +16,7 @@ import com.alphawallet.app.entity.ContractLocator; import com.alphawallet.app.entity.ContractType; import com.alphawallet.app.entity.CustomViewSettings; +import com.alphawallet.app.entity.ImageEntry; import com.alphawallet.app.entity.NetworkInfo; import com.alphawallet.app.entity.ServiceSyncCallback; import com.alphawallet.app.entity.Wallet; @@ -78,6 +80,7 @@ public class TokensService private ContractLocator focusToken; private final ConcurrentLinkedDeque unknownTokens; private final ConcurrentLinkedQueue baseTokenCheck; + private final ConcurrentLinkedQueue imagesForWrite; private long openSeaCheckId; private boolean appHasFocus; private static boolean walletStartup = false; @@ -102,6 +105,8 @@ public class TokensService private Disposable tokenStoreDisposable; @Nullable private Disposable openSeaQueryDisposable; + @Nullable + private Disposable imageWriter; private static boolean done = false; @@ -120,6 +125,7 @@ public TokensService(EthereumNetworkRepositoryType ethereumNetworkRepository, focusToken = null; this.unknownTokens = new ConcurrentLinkedDeque<>(); this.baseTokenCheck = new ConcurrentLinkedQueue<>(); + this.imagesForWrite = new ConcurrentLinkedQueue<>(); setCurrentAddress(ethereumNetworkRepository.getCurrentWalletAddress()); //set current wallet address at service startup appHasFocus = true; transferCheckChain = 0; @@ -403,7 +409,6 @@ public void stopUpdateCycle() if (queryUnknownTokensDisposable != null && !queryUnknownTokensDisposable.isDisposed()) { queryUnknownTokensDisposable.dispose(); } if (openSeaQueryDisposable != null && !openSeaQueryDisposable.isDisposed()) { openSeaQueryDisposable.dispose(); } - IconItem.resetCheck(); pendingChainMap.clear(); tokenStoreList.clear(); baseTokenCheck.clear(); @@ -502,6 +507,35 @@ private void startUnknownCheck() } } + private void startImageWrite() + { + if (imageWriter == null || imageWriter.isDisposed()) + { + imageWriter = Observable.interval(500, 500, TimeUnit.MILLISECONDS) + .doOnNext(l -> writeImages()).subscribe(); + } + } + + private void writeImages() + { + if (imagesForWrite.isEmpty()) + { + imageWriter.dispose(); + imageWriter = null; + } + else + { + Single.fromCallable(() -> { + List entries = new ArrayList<>(imagesForWrite); + imagesForWrite.clear(); + tokenRepository.addImageUrl(entries); + return ""; + }) + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.io()).subscribe().isDisposed(); + } + } + private void startupPass() { if (!walletStartup) return; @@ -540,9 +574,12 @@ public String getNetworkSymbol(long chainId) return info.symbol; } + //Add to write queue public void addTokenImageUrl(long networkId, String address, String imageUrl) { - tokenRepository.addImageUrl(networkId, address, imageUrl); + ImageEntry entry = new ImageEntry(networkId, address, imageUrl); + imagesForWrite.add(entry); + startImageWrite(); } public Single update(String address, long chainId, ContractType type) diff --git a/app/src/main/java/com/alphawallet/app/service/WalletConnectService.java b/app/src/main/java/com/alphawallet/app/service/WalletConnectService.java deleted file mode 100644 index 910dc5f936..0000000000 --- a/app/src/main/java/com/alphawallet/app/service/WalletConnectService.java +++ /dev/null @@ -1,514 +0,0 @@ -package com.alphawallet.app.service; - -import static com.alphawallet.app.C.WALLET_CONNECT_ADD_CHAIN; -import static com.alphawallet.app.C.WALLET_CONNECT_CLIENT_TERMINATE; -import static com.alphawallet.app.C.WALLET_CONNECT_COUNT_CHANGE; -import static com.alphawallet.app.C.WALLET_CONNECT_FAIL; -import static com.alphawallet.app.C.WALLET_CONNECT_NEW_SESSION; -import static com.alphawallet.app.C.WALLET_CONNECT_REQUEST; -import static com.alphawallet.app.C.WALLET_CONNECT_SWITCH_CHAIN; - -import android.app.Service; -import android.content.Intent; -import android.os.Binder; -import android.os.IBinder; -import android.text.TextUtils; - -import androidx.annotation.Nullable; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; - -import com.alphawallet.app.C; -import com.alphawallet.app.entity.WalletConnectActions; -import com.alphawallet.app.entity.walletconnect.SignType; -import com.alphawallet.app.entity.walletconnect.WCRequest; -import com.alphawallet.app.walletconnect.WCClient; -import com.alphawallet.app.web3.entity.WalletAddEthereumChainObject; - -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.TimeUnit; - -import io.reactivex.Observable; -import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; -import kotlin.Unit; -import timber.log.Timber; - -/** - * The purpose of this service is to manage the currently active WalletConnect sessions. Keep the connections alive and terminate where required. - * Doing this in an activity means the connection objects are subject to activity lifecycle events (Destroy etc). - */ -public class WalletConnectService extends Service -{ - private final static ConcurrentHashMap clientMap = new ConcurrentHashMap<>(); - private final static ConcurrentLinkedQueue signRequests = new ConcurrentLinkedQueue<>(); - private final static ConcurrentHashMap clientTimes = new ConcurrentHashMap<>(); - private WCRequest currentRequest = null; - - private static final String TAG = "WCClientSvs"; - - @Nullable - private Disposable messagePump; - - @Override - public int onStartCommand(Intent intent, int flags, int startId) - { - Timber.tag(TAG).d("SERVICE STARTING"); - - if (intent == null) - { - return Service.START_STICKY; - } - - try - { - int actionVal = Integer.parseInt(intent.getAction()); - WalletConnectActions action = WalletConnectActions.values()[actionVal]; - - switch (action) - { - case CONNECT: - Timber.tag(TAG).d("SERVICE CONNECT"); - break; - case APPROVE: - approveRequest(intent); - break; - case REJECT: - rejectRequest(intent); - break; - case DISCONNECT: - Timber.tag(TAG).d("SERVICE DISCONNECT"); - //kill any active connection - disconnectCurrentSessions(); - break; - case CLOSE: - Timber.tag(TAG).d("SERVICE CLOSE"); - //result.getData().getIntExtra(C.EXTRA_CHAIN_ID, -1); - String sessionId = intent.getStringExtra("session"); - disconnectSession(sessionId); - break; - case MSG_PUMP: - Timber.tag(TAG).d("SERVICE MSG PUMP"); - checkMessages(); - break; - case SWITCH_CHAIN: - Timber.tag(TAG).d("SERVICE SWITCH CHAIN"); - switchChain(intent); - break; - case ADD_CHAIN: - Timber.tag(TAG).d("SERVICE ADD CHAIN"); - addChain(intent); - break; - } - } - catch (Exception e) - { - Timber.e(e); - } - return START_STICKY; - } - - private final IBinder mBinder = new LocalBinder(); - - public WCRequest getPendingRequest(String sessionId) - { - WCRequest request = signRequests.poll(); - if (request != null && !request.sessionId.equals(sessionId)) - { - signRequests.add(request); // not for this client, put it back on the stack, at the back - request = null; - } - else if (request != null) - { - currentRequest = request; - } - - return request; - } - - public int getConnectionCount() - { - return clientMap.size(); - } - - public WCRequest getCurrentRequest() - { - return currentRequest; - } - - private void disconnectCurrentSessions() - { - for (WCClient client : clientMap.values()) - { - if (client.isConnected()) - { - client.killSession(); - } - } - } - - private void disconnectSession(String sessionId) - { - if (TextUtils.isEmpty(sessionId)) - { - disconnectCurrentSessions(); - } - else - { - WCClient client = clientMap.get(sessionId); - if (client != null) client.killSession(); - } - } - - //executed a pending request, remove from the queue - public void removePendingRequest(long id) - { - for (WCRequest rq : signRequests) - { - if (rq.id == id) - { - signRequests.remove(rq); - break; - } - } - } - - public class LocalBinder extends Binder - { - public WalletConnectService getService() - { - return WalletConnectService.this; - } - } - - @Nullable - private Disposable pingTimer; - - @Nullable - @Override - public IBinder onBind(Intent intent) - { - return mBinder; - } - - public WCClient getClient(String sessionId) - { - if (sessionId == null) - { - return null; - } - else - { - return clientMap.get(sessionId); - } - } - - public void putClient(String sessionId, WCClient client) - { - Timber.tag(TAG).d("Add session: %s", sessionId); - clientMap.put(sessionId, client); - broadcastConnectionCount(clientMap.size()); - clientTimes.put(sessionId, System.currentTimeMillis()); - setupClient(client); - startSessionPinger(); - } - - public void addClients(List clientList) - { - for (WCClient client : clientList) - { - String sessionId = client.sessionId(); - if (sessionId != null && clientMap.get(sessionId) == null) - { - Timber.d("WC: Add client: %s", sessionId); - putClient(sessionId, client); - } - } - } - - private void rejectRequest(Intent intent) - { - String sessionId = intent.getStringExtra("sessionId"); - long id = intent.getLongExtra("id", 0L); - String message = intent.getStringExtra("message"); - - WCClient c = clientMap.get(sessionId); - if (c != null && c.isConnected()) - { - c.rejectRequest(id, message); - } - } - - private void approveRequest(Intent intent) - { - String sessionId = intent.getStringExtra("sessionId"); - long id = intent.getLongExtra("id", 0L); - String message = intent.getStringExtra("message"); - - WCClient c = clientMap.get(sessionId); - - if (c != null && c.isConnected()) - { - c.approveRequest(id, message); - } - } - - private void setupClient(WCClient client) - { - client.setOnSessionRequest((id, peer) -> { - if (client.sessionId() == null) return Unit.INSTANCE; - setLastUsed(client); - signRequests.add(new WCRequest(client.sessionId(), id, peer, client.chainIdVal())); - broadcastSessionEvent(WALLET_CONNECT_NEW_SESSION, client.sessionId()); - Timber.tag(TAG).d("On Request: %s", peer.getName()); - return Unit.INSTANCE; - }); - - client.setOnFailure(throwable -> { - //alert UI - if (client.sessionId() == null) return Unit.INSTANCE; - Timber.tag(TAG).d("On Fail: %s", throwable.getMessage()); - //only add if no errors already in queue - if (queueHasNoErrors()) - { - signRequests.add(new WCRequest(client.sessionId(), throwable, client.chainIdVal())); - broadcastSessionEvent(WALLET_CONNECT_FAIL, client.sessionId()); - } - startMessagePump(); - return Unit.INSTANCE; - }); - - client.setOnDisconnect((code, reason) -> { - Timber.tag(TAG).d("Terminate session?"); - terminateClient(client.sessionId()); - client.resetState(); - return Unit.INSTANCE; - }); - - client.setOnEthSign((id, message) -> { - if (client.sessionId() == null) return Unit.INSTANCE; - setLastUsed(client); - WCRequest rq = new WCRequest(client.sessionId(), id, message); - Timber.tag(TAG).d("Sign Request: %s", message.toString()); - sendRequest(client, rq); - return Unit.INSTANCE; - }); - - client.setOnEthSignTransaction((id, transaction) -> { - if (client.sessionId() == null) return Unit.INSTANCE; - setLastUsed(client); - WCRequest rq = new WCRequest(client.sessionId(), id, transaction, true, client.chainIdVal()); - sendRequest(client, rq); - return Unit.INSTANCE; - }); - - client.setOnEthSendTransaction((id, transaction) -> { - if (client.sessionId() == null) return Unit.INSTANCE; - setLastUsed(client); - WCRequest rq = new WCRequest(client.sessionId(), id, transaction, false, client.chainIdVal()); - sendRequest(client, rq); - return Unit.INSTANCE; - }); - - client.setOnSwitchEthereumChain((requestId, chainId) -> { - Timber.tag(TAG).d("onSwitchEthereumChain: request.id: %s, sessionId: %s, chainId: %s", requestId, client.sessionId(), chainId); - // send broadcast to show dialog for switching chain - Intent i = new Intent(WALLET_CONNECT_SWITCH_CHAIN); - i.putExtra(C.EXTRA_WC_REQUEST_ID, requestId); - i.putExtra(C.EXTRA_SESSION_ID, client.sessionId()); - i.putExtra(C.EXTRA_CHAIN_ID, chainId); - i.putExtra(C.EXTRA_NAME, client.getPeerMeta().getName()); - sendWalletConnectBroadcast(i); - return Unit.INSTANCE; - }); - - client.setOnAddEthereumChain((requestId, chainObj) -> { - Timber.tag(TAG).d("onAddEthereumChain: requestId: %s, chainObj: %s", requestId, chainObj); - Intent i = new Intent(WALLET_CONNECT_ADD_CHAIN); - i.putExtra(C.EXTRA_WC_REQUEST_ID, requestId); - i.putExtra(C.EXTRA_SESSION_ID, client.sessionId()); - i.putExtra(C.EXTRA_CHAIN_OBJ, chainObj); - sendWalletConnectBroadcast(i); - return Unit.INSTANCE; - }); - } - - private void sendWalletConnectBroadcast(Intent i) - { - LocalBroadcastManager.getInstance(this).sendBroadcast(i); - } - - //TODO: Can we determine if AlphaWallet is running? If it is, no need to add this to the queue, - //TODO: as user will get the intent in walletConnectActionReceiver (repeat for below) - private void sendRequest(WCClient client, WCRequest rq) - { - Timber.d("sendRequest: sessionId: %s", client.sessionId()); - signRequests.add(rq); - //see if this connection is live, if so then bring WC request to foreground - switchToWalletConnectApprove(client.sessionId(), rq); - startMessagePump(); - } - - private void broadcastSessionEvent(String command, String sessionId) - { - Timber.d("broadcastSessionEvent: sessionId: %s, command: %s", sessionId, command); - Intent intent = new Intent(command); - intent.putExtra("sessionid", sessionId); - intent.putExtra("wcrequest", getPendingRequest(sessionId)); // pass WCRequest as parcelable in the intent - sendWalletConnectBroadcast(intent); - } - - private void broadcastConnectionCount(int count) - { - Intent intent = new Intent(WALLET_CONNECT_COUNT_CHANGE); - intent.putExtra("count", count); - sendWalletConnectBroadcast(intent); - } - - private void switchToWalletConnectApprove(String sessionId, WCRequest rq) - { - WCClient cc = clientMap.get(sessionId); - - if (cc != null) - { - Intent intent = new Intent(WALLET_CONNECT_REQUEST); - intent.putExtra("sessionid", sessionId); - intent.putExtra("wcrequest", rq); - sendWalletConnectBroadcast(intent); - - Timber.tag(TAG).d("Connected clients: %s", clientMap.size()); - } - } - - private void startSessionPinger() - { - if (pingTimer == null || pingTimer.isDisposed()) - { - pingTimer = Observable.interval(0, 30, TimeUnit.SECONDS) - .doOnNext(l -> ping()).subscribe(); - } - } - - private void ping() - { - for (String sessionKey : clientMap.keySet()) - { - WCClient c = clientMap.get(sessionKey); - if (c == null) return; - if (c.isConnected() && c.chainIdVal() != 0 && c.getAccounts() != null) - { - Timber.tag(TAG).d("Ping Key: %s", sessionKey); - c.updateSession(c.getAccounts(), c.chainIdVal(), true); - } - } - } - - public void terminateClient(String sessionKey) - { - broadcastSessionEvent(WALLET_CONNECT_CLIENT_TERMINATE, sessionKey); - clientMap.remove(sessionKey); - broadcastConnectionCount(clientMap.size()); - if (clientMap.size() == 0 && pingTimer != null && !pingTimer.isDisposed()) - { - Timber.tag(TAG).d("Stop timer & service"); - pingTimer.dispose(); - pingTimer = null; - stopSelf(); - } - } - - private void setLastUsed(WCClient c) - { - String sessionId = c.sessionId(); - if (sessionId != null) clientTimes.put(sessionId, System.currentTimeMillis()); - } - - private void startMessagePump() - { - if (messagePump != null && !messagePump.isDisposed()) messagePump.dispose(); - - messagePump = Observable.interval(2000, 2000, TimeUnit.MILLISECONDS) - .doOnNext(l -> checkMessages()) - .observeOn(Schedulers.newThread()).subscribe(); - } - - private void checkMessages() - { - WCRequest rq = signRequests.peek(); - if (rq != null) - { - WCClient cc = clientMap.get(rq.sessionId); - if (cc != null) - { - switchToWalletConnectApprove(rq.sessionId, rq); - } - } - else if (messagePump != null && !messagePump.isDisposed()) - { - messagePump.dispose(); - } - } - - private boolean queueHasNoErrors() - { - for (WCRequest rq : signRequests.toArray(new WCRequest[0])) - { - if (rq.type == SignType.FAILURE) return false; - } - - return true; - } - - private void switchChain(Intent intent) - { - long requestId = intent.getLongExtra(C.EXTRA_WC_REQUEST_ID, -1); - String id = intent.getStringExtra(C.EXTRA_SESSION_ID); - long chainId = intent.getLongExtra(C.EXTRA_CHAIN_ID, -1); - boolean approved = intent.getBooleanExtra(C.EXTRA_APPROVED, false); - boolean chainAvailable = intent.getBooleanExtra(C.EXTRA_CHAIN_AVAILABLE, true); - Timber.tag(TAG).d("sessionId: %s, chainId: %s, approved: %s", id, chainId, approved); - if (requestId != -1 && id != null && chainId != -1) - { - WCClient c = clientMap.get(id); - if (c != null) - { - c.switchChain(requestId, chainId, approved, chainAvailable); - } - else - { - Timber.tag(TAG).d("WCClient not found"); - } - } - } - - public void addChain(Intent intent) - { - long requestId = intent.getLongExtra(C.EXTRA_WC_REQUEST_ID, -1); - String id = intent.getStringExtra(C.EXTRA_SESSION_ID); - WalletAddEthereumChainObject chainObject = intent.getParcelableExtra(C.EXTRA_CHAIN_OBJ); - boolean chainAdded = intent.getBooleanExtra(C.EXTRA_APPROVED, false); - Timber.tag(TAG).d("sessionId: %s, chainObj: %s, chainAdded: %s", id, chainObject, chainAdded); - - if (requestId != -1) - { - WCClient c = clientMap.get(id); - if (c != null) - { - if (chainObject != null) - { - c.addChain(requestId, chainObject, chainAdded); - } - else - { - // reject with serverError - c.addChain(requestId, chainObject, false); - } - } - else - { - Timber.tag(TAG).d("WCClient not found"); - } - } - } -} diff --git a/app/src/main/java/com/alphawallet/app/ui/DappBrowserFragment.java b/app/src/main/java/com/alphawallet/app/ui/DappBrowserFragment.java index 080b4eb2eb..030575160c 100644 --- a/app/src/main/java/com/alphawallet/app/ui/DappBrowserFragment.java +++ b/app/src/main/java/com/alphawallet/app/ui/DappBrowserFragment.java @@ -83,7 +83,6 @@ import com.alphawallet.app.repository.TokensRealmSource; import com.alphawallet.app.repository.entity.RealmToken; import com.alphawallet.app.service.GasService; -import com.alphawallet.app.service.WalletConnectService; import com.alphawallet.app.ui.QRScanning.QRScannerActivity; import com.alphawallet.app.ui.widget.OnDappHomeNavClickListener; import com.alphawallet.app.ui.widget.entity.ActionSheetCallback; @@ -791,11 +790,11 @@ private void launchNetworkPicker() private void launchWalletConnectSessionCancel() { - String sessionId = walletConnectSession != null ? viewModel.getSessionId(walletConnectSession) : ""; + /*String sessionId = walletConnectSession != null ? viewModel.getSessionId(walletConnectSession) : ""; Intent bIntent = new Intent(getContext(), WalletConnectService.class); bIntent.setAction(String.valueOf(WalletConnectActions.CLOSE.ordinal())); bIntent.putExtra("session", sessionId); - requireActivity().startService(bIntent); + requireActivity().startService(bIntent);*/ reloadPage(); } diff --git a/app/src/main/java/com/alphawallet/app/ui/HomeActivity.java b/app/src/main/java/com/alphawallet/app/ui/HomeActivity.java index 6fa50fd33d..d4d65d1916 100644 --- a/app/src/main/java/com/alphawallet/app/ui/HomeActivity.java +++ b/app/src/main/java/com/alphawallet/app/ui/HomeActivity.java @@ -570,7 +570,7 @@ protected void onResume() { super.onResume(); setWCConnect(); - viewModel.prepare(this); + viewModel.prepare(); viewModel.getWalletName(this); viewModel.setErrorCallback(this); if (homeReceiver == null) @@ -913,15 +913,6 @@ public void resetTransactions() getFragment(ACTIVITY).resetTransactions(); } - @Override - public void openWalletConnect(String sessionId) - { - Intent intent = new Intent(getApplication(), WalletConnectActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - intent.putExtra("session", sessionId); - startActivity(intent); - } - private void hideDialog() { if (dialog != null && dialog.isShowing()) diff --git a/app/src/main/java/com/alphawallet/app/ui/MyAddressActivity.java b/app/src/main/java/com/alphawallet/app/ui/MyAddressActivity.java index b8a4845189..8b04ef4c22 100644 --- a/app/src/main/java/com/alphawallet/app/ui/MyAddressActivity.java +++ b/app/src/main/java/com/alphawallet/app/ui/MyAddressActivity.java @@ -212,7 +212,7 @@ private void showPointOfSaleMode() private void setupPOSMode(NetworkInfo info) { if (token == null) token = viewModel.getTokenService().getToken(info.chainId, wallet.address); - amountInput.setupToken(token, viewModel.getAssetDefinitionService(), viewModel.getTokenService(), this); + amountInput.setupToken(token, viewModel.getTokenService(), this); amountInput.setAmount(""); updateCryptoAmount(BigDecimal.ZERO); } @@ -340,7 +340,7 @@ public void onWindowFocusChanged(boolean hasFocus) } else { - amountInput.setupToken(token, viewModel.getAssetDefinitionService(), viewModel.getTokenService(), this); + amountInput.setupToken(token, viewModel.getTokenService(), this); } } } diff --git a/app/src/main/java/com/alphawallet/app/ui/NFTAssetDetailActivity.java b/app/src/main/java/com/alphawallet/app/ui/NFTAssetDetailActivity.java index 56f2ad7474..58ff870fc2 100644 --- a/app/src/main/java/com/alphawallet/app/ui/NFTAssetDetailActivity.java +++ b/app/src/main/java/com/alphawallet/app/ui/NFTAssetDetailActivity.java @@ -5,6 +5,7 @@ import static com.alphawallet.app.widget.AWalletAlertDialog.WARNING; import static java.util.Collections.singletonList; +import android.annotation.SuppressLint; import android.content.Intent; import android.os.Bundle; import android.text.Html; @@ -90,6 +91,7 @@ public class NFTAssetDetailActivity extends BaseActivity implements StandardFunc private NFTAttributeLayout nftAttributeLayout; private NFTAttributeLayout tsAttributeLayout; private TextView tokenDescription; + @SuppressLint("RestrictedApi") private ActionMenuItemView refreshMenu; private ProgressBar progressBar; private TokenInfoCategoryView descriptionLabel; @@ -114,7 +116,7 @@ public class NFTAssetDetailActivity extends BaseActivity implements StandardFunc private boolean triggeredReload; private long chainId; private Web3TokenView tokenScriptView; - private boolean usingNativeTokenScript = false; + private boolean usingNativeTokenScript = true; @Override protected void onCreate(@Nullable Bundle savedInstanceState) @@ -309,7 +311,7 @@ private void setup() if (!viewModel.getUseTSViewer()) { TokenDefinition td = viewModel.getAssetDefinitionService().getAssetDefinition(this.token); - this.usingNativeTokenScript = td.nameSpace != null; + this.usingNativeTokenScript = (td != null && td.nameSpace != null); } if (asset != null && asset.isAttestation()) diff --git a/app/src/main/java/com/alphawallet/app/ui/QRScanning/QRScannerActivity.java b/app/src/main/java/com/alphawallet/app/ui/QRScanning/QRScannerActivity.java index 3945e9f5bc..ad1ef3d414 100644 --- a/app/src/main/java/com/alphawallet/app/ui/QRScanning/QRScannerActivity.java +++ b/app/src/main/java/com/alphawallet/app/ui/QRScanning/QRScannerActivity.java @@ -26,7 +26,6 @@ import com.alphawallet.app.entity.analytics.QrScanResultType; import com.alphawallet.app.entity.analytics.QrScanSource; import com.alphawallet.app.ui.BaseActivity; -import com.alphawallet.app.ui.WalletConnectActivity; import com.alphawallet.app.ui.WalletConnectV2Activity; import com.alphawallet.app.viewmodel.QrScannerViewModel; import com.alphawallet.app.walletconnect.util.WalletConnectHelper; @@ -189,18 +188,8 @@ private void displayErrorDialog(String title, String errorMessage) private void startWalletConnect(String qrCode) { - Intent intent; - if (WalletConnectHelper.isWalletConnectV1(qrCode)) - { - intent = new Intent(this, WalletConnectActivity.class); - intent.putExtra("qrCode", qrCode); - intent.putExtra(C.EXTRA_CHAIN_ID, chainIdOverride); - } - else - { - intent = new Intent(this, WalletConnectV2Activity.class); - intent.putExtra("url", qrCode); - } + Intent intent = new Intent(this, WalletConnectV2Activity.class); + intent.putExtra("url", qrCode); startActivity(intent); setResult(WALLET_CONNECT); finish(); @@ -209,6 +198,7 @@ private void startWalletConnect(String qrCode) @Override public void onBackPressed() { + super.onBackPressed(); viewModel.track(Analytics.Action.SCAN_QR_CODE_CANCELLED); Intent intent = new Intent(); setResult(Activity.RESULT_CANCELED, intent); diff --git a/app/src/main/java/com/alphawallet/app/ui/SearchActivity.java b/app/src/main/java/com/alphawallet/app/ui/SearchActivity.java index 19916e7a69..31d52c4409 100644 --- a/app/src/main/java/com/alphawallet/app/ui/SearchActivity.java +++ b/app/src/main/java/com/alphawallet/app/ui/SearchActivity.java @@ -22,9 +22,7 @@ import com.alphawallet.app.ui.widget.TokensAdapterCallback; import com.alphawallet.app.ui.widget.adapter.TokensAdapter; import com.alphawallet.app.ui.widget.entity.SearchToolbarCallback; -import com.alphawallet.app.util.Utils; import com.alphawallet.app.viewmodel.WalletViewModel; -import com.alphawallet.app.widget.AWalletAlertDialog; import com.alphawallet.app.widget.SearchToolbar; import org.web3j.crypto.WalletUtils; diff --git a/app/src/main/java/com/alphawallet/app/ui/SendActivity.java b/app/src/main/java/com/alphawallet/app/ui/SendActivity.java index d26e0b051d..181b1d7c98 100644 --- a/app/src/main/java/com/alphawallet/app/ui/SendActivity.java +++ b/app/src/main/java/com/alphawallet/app/ui/SendActivity.java @@ -193,12 +193,6 @@ else if (item.getItemId() == R.id.action_show_contract) return false; } - @Override - public void onBackPressed() - { - onBack(); - } - @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { @@ -295,16 +289,6 @@ else if (requestCode == C.BARCODE_READER_REQUEST_CODE) } } - private void startWalletConnect(String qrCode) - { - Intent intent = new Intent(this, WalletConnectActivity.class); - intent.putExtra("qrCode", qrCode); - intent.putExtra(C.EXTRA_CHAIN_ID, token.tokenInfo.chainId); - startActivity(intent); - setResult(RESULT_OK); - finish(); - } - private void showCameraDenied() { if (dialog != null && dialog.isShowing()) dialog.dismiss(); @@ -380,7 +364,7 @@ else if (result.type != EIP681Type.ADDRESS && result.chainId != token.tokenInfo. sendText.setText(R.string.transfer_request); token = viewModel.getToken(result.chainId, wallet.address); addressInput.setAddress(result.getAddress()); - amountInput.setupToken(token, viewModel.getAssetDefinitionService(), viewModel.getTokenService(), this); + amountInput.setupToken(token, viewModel.getTokenService(), this); amountInput.setAmount(ethAmount); setupTokenContent(); break; @@ -398,7 +382,7 @@ else if (resultToken.isERC20()) //ERC20 send request token = resultToken; setupTokenContent(); - amountInput.setupToken(token, viewModel.getAssetDefinitionService(), viewModel.getTokenService(), this); + amountInput.setupToken(token, viewModel.getTokenService(), this); //convert token amount into scaled value String convertedAmount = Convert.getConvertedValue(result.tokenAmount, token.tokenInfo.decimals); amountInput.setAmount(convertedAmount); @@ -432,7 +416,7 @@ private void showChainChangeDialog(long chainId) dialog.setButtonListener(v -> { //we should change the chain. token = viewModel.getToken(chainId, token.getAddress()); - amountInput.setupToken(token, viewModel.getAssetDefinitionService(), viewModel.getTokenService(), this); + amountInput.setupToken(token, viewModel.getTokenService(), this); dialog.dismiss(); validateEIP681Request(currentResult, false); }); @@ -488,7 +472,7 @@ protected void onDestroy() private void setupTokenContent() { amountInput = findViewById(R.id.input_amount); - amountInput.setupToken(token, viewModel.getAssetDefinitionService(), viewModel.getTokenService(), this); + amountInput.setupToken(token, viewModel.getTokenService(), this); addressInput = findViewById(R.id.input_address); addressInput.setAddressCallback(this); addressInput.setChainOverrideForWalletConnect(token.tokenInfo.chainId); diff --git a/app/src/main/java/com/alphawallet/app/ui/TransactionDetailActivity.java b/app/src/main/java/com/alphawallet/app/ui/TransactionDetailActivity.java index d1c10aba20..6e07dd5adc 100644 --- a/app/src/main/java/com/alphawallet/app/ui/TransactionDetailActivity.java +++ b/app/src/main/java/com/alphawallet/app/ui/TransactionDetailActivity.java @@ -244,7 +244,7 @@ private void setupTokenDetails() Token targetToken = viewModel.getToken(transaction.chainId, TextUtils.isEmpty(tokenAddress) ? transaction.to : tokenAddress); if (targetToken.isEthereum()) return; tokenDetailsLayout.setVisibility(View.VISIBLE); - icon.bindData(targetToken, viewModel.getTokenService()); + icon.bindData(targetToken); address.setText(Keys.toChecksumAddress(targetToken.getAddress())); tokenName.setText(targetToken.getFullName()); } diff --git a/app/src/main/java/com/alphawallet/app/ui/WalletConnectActivity.java b/app/src/main/java/com/alphawallet/app/ui/WalletConnectActivity.java deleted file mode 100644 index 288e257240..0000000000 --- a/app/src/main/java/com/alphawallet/app/ui/WalletConnectActivity.java +++ /dev/null @@ -1,1565 +0,0 @@ -package com.alphawallet.app.ui; - -import static com.alphawallet.ethereum.EthereumNetworkBase.MAINNET_ID; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.text.TextUtils; -import android.text.format.DateUtils; -import android.view.MenuItem; -import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ProgressBar; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.activity.result.ActivityResultLauncher; -import androidx.activity.result.contract.ActivityResultContracts; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.lifecycle.Lifecycle; -import androidx.lifecycle.ViewModelProvider; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; - -import com.alphawallet.app.C; -import com.alphawallet.app.R; -import com.alphawallet.app.analytics.Analytics; -import com.alphawallet.app.entity.ActionSheetStatus; -import com.alphawallet.app.entity.AnalyticsProperties; -import com.alphawallet.app.entity.CryptoFunctions; -import com.alphawallet.app.entity.NetworkInfo; -import com.alphawallet.app.entity.SignAuthenticationCallback; -import com.alphawallet.app.entity.StandardFunctionInterface; -import com.alphawallet.app.entity.TransactionReturn; -import com.alphawallet.app.entity.Wallet; -import com.alphawallet.app.entity.WalletType; -import com.alphawallet.app.entity.analytics.ActionSheetSource; -import com.alphawallet.app.entity.tokens.Token; -import com.alphawallet.app.entity.walletconnect.SignType; -import com.alphawallet.app.entity.walletconnect.WCRequest; -import com.alphawallet.app.repository.EthereumNetworkBase; -import com.alphawallet.app.repository.SignRecord; -import com.alphawallet.app.service.GasService; -import com.alphawallet.app.ui.widget.entity.ActionSheetCallback; -import com.alphawallet.app.viewmodel.WalletConnectViewModel; -import com.alphawallet.app.walletconnect.AWWalletConnectClient; -import com.alphawallet.app.walletconnect.WCClient; -import com.alphawallet.app.walletconnect.WCSession; -import com.alphawallet.app.walletconnect.entity.WCEthereumSignMessage; -import com.alphawallet.app.walletconnect.entity.WCEthereumTransaction; -import com.alphawallet.app.walletconnect.entity.WCPeerMeta; -import com.alphawallet.app.walletconnect.entity.WCUtils; -import com.alphawallet.app.walletconnect.entity.WalletConnectCallback; -import com.alphawallet.app.web3.entity.Address; -import com.alphawallet.app.web3.entity.WalletAddEthereumChainObject; -import com.alphawallet.app.web3.entity.Web3Transaction; -import com.alphawallet.app.widget.AWalletAlertDialog; -import com.alphawallet.app.widget.ActionSheet; -import com.alphawallet.app.widget.ActionSheetDialog; -import com.alphawallet.app.widget.ActionSheetSignDialog; -import com.alphawallet.app.widget.ChainName; -import com.alphawallet.app.widget.FunctionButtonBar; -import com.alphawallet.app.widget.SignTransactionDialog; -import com.alphawallet.app.widget.TokenIcon; -import com.alphawallet.hardware.SignatureFromKey; -import com.alphawallet.token.entity.EthereumMessage; -import com.alphawallet.token.entity.EthereumTypedMessage; -import com.alphawallet.token.entity.SignMessageType; -import com.alphawallet.token.entity.Signable; -import com.bumptech.glide.Glide; -import com.google.gson.Gson; - -import org.jetbrains.annotations.NotNull; -import org.web3j.utils.Numeric; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.UUID; - -import javax.inject.Inject; - -import dagger.hilt.android.AndroidEntryPoint; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.schedulers.Schedulers; -import kotlin.Unit; -import timber.log.Timber; - -@AndroidEntryPoint -public class WalletConnectActivity extends BaseActivity implements ActionSheetCallback, StandardFunctionInterface, WalletConnectCallback -{ - public static final String WC_LOCAL_PREFIX = "wclocal:"; - public static final String WC_INTENT = "wcintent:"; - private static final String TAG = "WCClient"; - private static final String DEFAULT_ICON = "https://example.walletconnect.org/favicon.ico"; - private static final long CONNECT_TIMEOUT = 10 * DateUtils.SECOND_IN_MILLIS; // 10 Seconds timeout - private final Handler handler = new Handler(Looper.getMainLooper()); - private final long switchChainDialogCallbackId = 1; - private WalletConnectViewModel viewModel; - private LocalBroadcastManager broadcastManager; - private WCClient client; - private WCSession session; - private WCPeerMeta peerMeta; - private WCPeerMeta remotePeerMeta; - private ActionSheet confirmationDialog; - ActivityResultLauncher getGasSettings = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), - result -> confirmationDialog.setCurrentGasIndex(result)); - private AddEthereumChainPrompt addEthereumChainPrompt; - // data for switch chain request - private long switchChainRequestId; // rpc request id - private long switchChainId; // new chain to switch to - private String name; // remote peer name - private String currentSessionId; // sessionId for which chain is switched - private boolean chainAvailable; // flag denoting chain available in AW or not - private ImageView icon; - private TextView peerName; - private TextView peerUrl; - private TextView statusText; - private TextView textName; - private TextView txCount; - private ChainName chainName; - private TokenIcon chainIcon; - private ProgressBar progressBar; - private LinearLayout infoLayout; - private LinearLayout txCountLayout; - private FunctionButtonBar functionBar; - private boolean fromDappBrowser = false; //if using this from dappBrowser (which is a bit strange but could happen) then return back to browser once signed - private boolean fromPhoneBrowser = false; //if from phone browser, clicking 'back' should take user back to dapp running on the phone's browser, - // -- according to WalletConnect's expected UX docs. - private boolean fromSessionActivity = false; - private String qrCode; - private SignAuthenticationCallback signCallback; - private long lastId; - private String signData; - private WCEthereumSignMessage.WCSignType signType; - private long chainIdOverride; - - @Inject - AWWalletConnectClient awWalletConnectClient; - - ActivityResultLauncher getNetwork = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), - result -> { - if (result.getData() == null) return; - chainIdOverride = result.getData().getLongExtra(C.EXTRA_CHAIN_ID, MAINNET_ID); - confirmationDialog.updateChain(chainIdOverride); - }); - private boolean waitForWalletConnectSession = false; - private long requestId = 0; - private AWalletAlertDialog dialog = null; - private final BroadcastReceiver walletConnectActionReceiver = new BroadcastReceiver() - { - @Override - public void onReceive(Context context, Intent intent) - { - Timber.tag(TAG).d("Received message"); - String action = intent.getAction(); - switch (action) - { - case C.WALLET_CONNECT_REQUEST: - case C.WALLET_CONNECT_NEW_SESSION: - case C.WALLET_CONNECT_FAIL: - Timber.tag(TAG).d("MSG: %s", action); -// getPendingRequest(); - WCRequest wcRequest = (WCRequest) intent.getParcelableExtra("wcrequest"); - if (wcRequest != null) - { - executedPendingRequest(wcRequest.id); - receiveRequest(wcRequest); - } - else - { - // something went wrong - } - break; - case C.WALLET_CONNECT_CLIENT_TERMINATE: - String sessionId = intent.getStringExtra("sessionid"); - Timber.tag(TAG).d("MSG: TERMINATE: %s", sessionId); - if (viewModel != null) - { - viewModel.endSession(sessionId); - } - if (getSessionId() != null && getSessionId().equals(sessionId)) - { - setupClient(sessionId); - finish(); - } - break; - case C.WALLET_CONNECT_SWITCH_CHAIN: - Timber.tag(TAG).d("MSG: SWITCH CHAIN: "); - onSwitchChainRequest(intent); - break; - case C.WALLET_CONNECT_ADD_CHAIN: - Timber.tag(TAG).d("MSG: ADD CHAIN"); - onAddChainRequest(intent); - break; - } - } - }; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - setContentView(R.layout.activity_wallet_connect); - - toolbar(); - - setTitle(getString(R.string.title_wallet_connect)); - - initViews(); - - initViewModel(); - - Timber.tag(TAG).d("Starting Activity: %s", getSessionId()); - - retrieveQrCode(); - viewModel.prepare(); - - if (savedInstanceState != null) restoreState(savedInstanceState); - } - - private void restoreState(Bundle savedInstance) - { - //Orientation change? - - if (savedInstance.containsKey("ORIENTATION") && savedInstance.containsKey("SESSIONID")) - { - int oldOrientation = savedInstance.getInt("ORIENTATION"); - int newOrientation = getResources().getConfiguration().orientation; - - if (oldOrientation != newOrientation) - { - requestId = savedInstance.getLong("SESSIONID"); - String sessionId = savedInstance.getString("SESSIONIDSTR"); - session = viewModel.getSession(sessionId); - - if (savedInstance.containsKey("TRANSACTION")) - { - Web3Transaction w3Tx = savedInstance.getParcelable("TRANSACTION"); - chainIdOverride = savedInstance.getLong("CHAINID"); - - //kick off transaction - final ActionSheetDialog confDialog = generateTransactionRequest(w3Tx, chainIdOverride); - if (confDialog != null) - { - confirmationDialog = confDialog; - confirmationDialog.show(); - } - } - else if (savedInstance.containsKey("SIGNDATA")) - { - signData = savedInstance.getString("SIGNDATA"); - signType = WCEthereumSignMessage.WCSignType.values()[savedInstance.getInt("SIGNTYPE")]; - lastId = savedInstance.getLong("LASTID"); - String peerUrl = savedInstance.getString("PEERURL"); - Signable signable = null; - - //kick off sign - switch (signType) - { - case MESSAGE: - case PERSONAL_MESSAGE: - signable = new EthereumMessage(signData, peerUrl, lastId, SignMessageType.SIGN_PERSONAL_MESSAGE); - break; - case TYPED_MESSAGE: - signable = new EthereumTypedMessage(signData, peerUrl, lastId, new CryptoFunctions()); - break; - } - - doSignMessage(signable); - } - } - } - } - - @Override - protected void onNewIntent(Intent intent) - { - super.onNewIntent(intent); - Bundle data = intent.getExtras(); - chainIdOverride = 0; - if (data != null) - { - //detect new intent from service - String sessionIdFromService = data.getString("session"); - if (sessionIdFromService != null) - { - handleStartFromWCMessage(sessionIdFromService); - } - else - { - handleStartDirectFromQRScan(); - } - } - } - - private void onServiceReady(Boolean b) - { - if (waitForWalletConnectSession) - { - waitForWalletConnectSession = false; - handleStartDirectFromQRScan(); - } - } - - private void handleStartDirectFromQRScan() - { - String sessionId = getSessionId(); - retrieveQrCode(); - String newSessionId = getSessionId(); - Timber.tag(TAG).d("Received New Intent: %s (%s)", newSessionId, sessionId); - viewModel.getClient(this, newSessionId, client -> { - if (client == null || !client.isConnected()) - { - //TODO: ERROR! - showErrorDialogTerminate(getString(R.string.session_terminated)); - infoLayout.setVisibility(View.GONE); - } - else - { - //setup the screen - Timber.tag(TAG).d("Resume Connection session: %s", newSessionId); - setClient(client); - } - }); - } - - private void handleStartFromWCMessage(String sessionIdFromService) - { - //is different from current sessionId? - if (!sessionIdFromService.equals(getSessionId())) - { - //restore different session - session = viewModel.getSession(sessionIdFromService); - viewModel.getClient(this, sessionIdFromService, this::setClient); - qrCode = null; - fromDappBrowser = false; - } - else - { - //init UI - displaySessionStatus(sessionIdFromService); - } - } - - private void setClient(WCClient receivedClient) - { - client = receivedClient; - displaySessionStatus(client.sessionId()); - } - - private void retrieveQrCode() - { - Bundle data = getIntent().getExtras(); - if (data != null) - { - String qrCode = data.getString("qrCode"); - String sessionId = data.getString("session"); - chainIdOverride = data.getLong(C.EXTRA_CHAIN_ID, 0); - if (sessionId != null) fromSessionActivity = true; - if (!TextUtils.isEmpty(qrCode)) - { - parseSessionCode(data.getString("qrCode")); - } - else if (!TextUtils.isEmpty(sessionId)) - { - session = viewModel.getSession(sessionId); - } - } - else - { - Toast.makeText(this, "Error retrieving QR code", Toast.LENGTH_SHORT).show(); - finish(); - } - } - - private void parseSessionCode(String wcCode) - { - if (wcCode != null && wcCode.startsWith(WC_LOCAL_PREFIX)) - { - wcCode = wcCode.replace(WC_LOCAL_PREFIX, ""); - fromDappBrowser = true; - } - else if (wcCode != null && wcCode.startsWith(WC_INTENT)) - { - wcCode = wcCode.replace(WC_INTENT, ""); - fromPhoneBrowser = true; //don't use this yet, but could use it for switching between apps - } - this.qrCode = wcCode; - session = WCSession.Companion.from(qrCode); - Timber.d("WCClient: %s", qrCode); - } - - private void initViewModel() - { - viewModel = new ViewModelProvider(this) - .get(WalletConnectViewModel.class); - - viewModel.defaultWallet().observe(this, this::onDefaultWallet); - viewModel.serviceReady().observe(this, this::onServiceReady); - viewModel.transactionFinalised().observe(this, this::txWritten); - viewModel.transactionError().observe(this, this::txError); - viewModel.transactionSigned().observe(this, this::txSigned); - - viewModel.startService(this); - } - - private void initViews() - { - progressBar = findViewById(R.id.progress); - infoLayout = findViewById(R.id.layout_info); - icon = findViewById(R.id.icon); - peerName = findViewById(R.id.peer_name); - peerUrl = findViewById(R.id.peer_url); - statusText = findViewById(R.id.connection_status); - textName = findViewById(R.id.text_name); - txCountLayout = findViewById(R.id.layout_tx_count); - txCount = findViewById(R.id.tx_count); - chainName = findViewById(R.id.chain_name); - chainIcon = findViewById(R.id.chain_icon); - - progressBar.setVisibility(View.VISIBLE); - infoLayout.setVisibility(View.GONE); - - functionBar = findViewById(R.id.layoutButtons); - functionBar.setupFunctions(this, new ArrayList<>(Collections.singletonList(R.string.action_end_session))); - functionBar.setVisibility(View.GONE); - } - - @Override - public void handleClick(String action, int id) - { - if (id == R.string.action_end_session) - { - endSessionDialog(); - } - } - - //TODO: Refactor this into elements - this function is unmaintainable - private void onDefaultWallet(Wallet wallet) - { - Timber.tag(TAG).d("Open Connection: %s", getSessionId()); - - String peerId; - String sessionId = getSessionId(); - String connectionId = viewModel.getRemotePeerId(sessionId); - if (!TextUtils.isEmpty(viewModel.getWallet().address)) - { - if (connectionId == null && session != null) //new session request - { - Timber.tag(TAG).d("New Session: %s", getSessionId()); - //new connection, create a random ID to identify us to the remotePeer. - peerId = UUID.randomUUID().toString(); //Create a new ID for our side of this session. The remote peer uses this ID to identify us - connectionId = null; //connectionId is only relevant for resuming a session - } - else //existing session, rebuild the session data - { - //try to retrieve the session from database - session = viewModel.getSession(sessionId); - displaySessionStatus(sessionId); - - viewModel.getClient(this, sessionId, client -> { - Timber.tag(TAG).d("Resume Session: %s", getSessionId()); - - if (client == null && fromSessionActivity) - { - functionBar.setVisibility(View.GONE); - return; - } - else if (client == null || !client.isConnected()) - { - if (client == null || (!fromSessionActivity && session == null)) - { - showErrorDialogTerminate(getString(R.string.session_terminated)); - infoLayout.setVisibility(View.GONE); - } - else - { - //attempt to restart the session; this will allow session 'force' close - restartSession(session); - } - } - else - { -// getPendingRequest(); - setClient(client); - } - }); - - return; - } - - Timber.tag(TAG).d("connect: peerID %s", peerId); - Timber.tag(TAG).d("connect: remotePeerID %s", connectionId); - - initWalletConnectPeerMeta(); - initWalletConnectClient(); - initWalletConnectSession(peerId, connectionId); - } - } - - private void restartSession(WCSession session) - { - String sessionId = session.getTopic(); - client = WCUtils.createWalletConnectSession(this, viewModel.getWallet(), - session, viewModel.getPeerId(sessionId), viewModel.getRemotePeerId(sessionId)); - viewModel.putClient(this, sessionId, client); - } - - @SuppressWarnings("MethodOnlyUsedFromInnerClass") - private void executedPendingRequest(long id) - { - viewModel.removePendingRequest(this, id); - } - - @Override - public boolean receiveRequest(WCRequest rq) - { - if (rq != null) - { - requestId = rq.id; - long useChainId = viewModel.getChainId(getSessionId()); - switch (rq.type) - { - case MESSAGE: - if (watchOnly(rq.id)) return false; - runOnUiThread(() -> { - onEthSign(rq.id, rq.sign, useChainId); - }); - break; - case SIGN_TX: - if (watchOnly(rq.id)) return false; - runOnUiThread(() -> { - onEthSignTransaction(rq.id, rq.tx, useChainId); - }); - break; - case SEND_TX: - if (watchOnly(rq.id)) return false; - runOnUiThread(() -> { - onEthSendTransaction(rq.id, rq.tx, useChainId); - }); - break; - case FAILURE: - runOnUiThread(() -> { - onFailure(rq.throwable); - }); - break; - case SESSION_REQUEST: - Timber.tag(TAG).d("On Request: %s", rq.peer.getName()); - runOnUiThread(() -> { - onSessionRequest(rq.id, rq.peer, rq.chainId); - }); - break; - } - } - - return true; - } - - private boolean watchOnly(long id) - { - if (!viewModel.getWallet().canSign()) - { - closeErrorDialog(); - dialog = new AWalletAlertDialog(this, AWalletAlertDialog.ERROR); - dialog.setTitle(R.string.watch_wallet); - dialog.setButton(R.string.action_close, v -> { - viewModel.rejectRequest(getApplication(), getSessionId(), - id, getString(R.string.message_authentication_failed)); - dialog.dismiss(); - }); - dialog.setCancelable(false); - dialog.show(); - return true; - } - else - { - return false; - } - } - - private void closeErrorDialog() - { - if (dialog != null && dialog.isShowing()) - { - dialog.dismiss(); - } - } - - private void startMessageCheck() - { - IntentFilter filter = new IntentFilter(C.WALLET_CONNECT_REQUEST); - filter.addAction(C.WALLET_CONNECT_NEW_SESSION); - filter.addAction(C.WALLET_CONNECT_FAIL); - filter.addAction(C.WALLET_CONNECT_CLIENT_TERMINATE); - filter.addAction(C.WALLET_CONNECT_SWITCH_CHAIN); - filter.addAction(C.WALLET_CONNECT_ADD_CHAIN); - if (broadcastManager == null) broadcastManager = LocalBroadcastManager.getInstance(this); - broadcastManager.registerReceiver(walletConnectActionReceiver, filter); - } - - private String getSessionId() - { - if (qrCode != null) - { - String uriString = qrCode.replace("wc:", "wc://"); - return Uri.parse(uriString).getUserInfo(); - } - else if (session != null) - { - return session.getTopic(); - } - else - { - return null; - } - } - - private void initWalletConnectSession(String peerId, String connectionId) - { - if (session == null) - { - //error situation! - invalidSession(); - return; - } - - Timber.tag(TAG).d("Connect: %s (%s)", getSessionId(), connectionId); - client.connect(session, peerMeta, peerId, connectionId); - - client.setOnFailure(throwable -> { - Timber.tag(TAG).d("On Fail: %s", throwable.getMessage()); - showErrorDialog("Error: " + throwable.getMessage()); - return Unit.INSTANCE; - }); - - handler.postDelayed(() -> { - //Timeout check - if (client != null && client.chainIdVal() == 0 && (dialog == null || !dialog.isShowing())) - { - //show timeout - showTimeoutDialog(); - } - }, CONNECT_TIMEOUT); - } - - private void invalidSession() - { - closeErrorDialog(); - dialog = new AWalletAlertDialog(this, AWalletAlertDialog.ERROR); - dialog.setTitle(R.string.invalid_walletconnect_session); - dialog.setMessage(R.string.restart_walletconnect_session); - dialog.setButton(R.string.dialog_ok, v -> { - dialog.dismiss(); - finish(); - }); - dialog.setCancelable(false); - dialog.show(); - - viewModel.trackError(Analytics.Error.WALLET_CONNECT, getString(R.string.invalid_walletconnect_session)); - } - - private void initWalletConnectPeerMeta() - { - peerMeta = new WCPeerMeta( - getString(R.string.app_name), - C.ALPHAWALLET_WEB, - viewModel.getWallet().address, - new ArrayList<>(Collections.singleton(C.ALPHAWALLET_LOGO_URI)) - ); - } - - private void initWalletConnectClient() - { - client = new WCClient(); - - client.setOnWCOpen(peerId -> { - viewModel.putClient(this, getSessionId(), client); - Timber.tag(TAG).d("On Open: %s", peerId); - return Unit.INSTANCE; - }); - } - - @Override - protected void onSaveInstanceState(@NotNull Bundle state) - { - super.onSaveInstanceState(state); - //need to preserve the orientation and current signing request - state.putInt("ORIENTATION", getResources().getConfiguration().orientation); - state.putLong("SESSIONID", requestId); - state.putString("SESSIONIDSTR", getSessionId()); - if (confirmationDialog != null && confirmationDialog.isShowing() && confirmationDialog.getTransaction() != null) - { - state.putParcelable("TRANSACTION", confirmationDialog.getTransaction()); - state.putLong("CHAINID", viewModel.getChainId(getSessionId())); - } - if (confirmationDialog != null && confirmationDialog.isShowing() && signData != null) - { - state.putString("SIGNDATA", signData); - state.putInt("SIGNTYPE", signType.ordinal()); - state.putLong("LASTID", lastId); - state.putString("PEERURL", peerUrl.getText().toString()); - } - } - - private void setupClient(final String sessionId) - { - viewModel.getClient(this, sessionId, client -> - runOnUiThread(() -> { - if (client == null || !client.isConnected()) - { - statusText.setText(R.string.not_connected); - statusText.setTextColor(getColor(R.color.error)); - } - else - { - statusText.setText(R.string.online); - statusText.setTextColor(getColor(R.color.positive)); - } - })); - } - - private void displaySessionStatus(String sessionId) - { - progressBar.setVisibility(View.GONE); - functionBar.setVisibility(View.VISIBLE); - infoLayout.setVisibility(View.VISIBLE); - WCPeerMeta remotePeerData = viewModel.getRemotePeer(sessionId); - this.remotePeerMeta = remotePeerData; // init meta to access in other places - if (remotePeerData != null) - { - if (remotePeerData.getIcons().isEmpty()) - { - icon.setImageResource(R.drawable.ic_coin_eth_small); - } - else - { - Glide.with(this) - .load(remotePeerData.getIcons().get(0)) - .circleCrop() - .into(icon); - } - peerName.setText(remotePeerData.getName()); - textName.setText(remotePeerData.getName()); - peerUrl.setText(remotePeerData.getUrl()); - chainName.setChainID(viewModel.getChainId(sessionId)); - chainIcon.setVisibility(View.VISIBLE); - chainIcon.bindData(viewModel.getChainId(sessionId)); - viewModel.startGasCycle(viewModel.getChainId(sessionId)); - updateSignCount(); - } - } - - private void onSessionRequest(Long id, WCPeerMeta peer, long chainId) - { - if (peer == null) - { - finish(); - } - - closeErrorDialog(); - - closeConfirmationDialog(); - - String displayIcon = (peer.getIcons().size() > 0) ? peer.getIcons().get(0) : DEFAULT_ICON; - - chainIdOverride = chainIdOverride > 0 ? chainIdOverride : (chainId > 0 ? chainId : MAINNET_ID); - - displaySessionRequestDetails(peer, chainIdOverride, displayIcon); - - confirmationDialog = new ActionSheetDialog(this, peer, chainIdOverride, displayIcon, this); - if (confirmationDialog.getActionSheetStatus() == ActionSheetStatus.ERROR_INVALID_CHAIN) - { - showErrorDialogUnsupportedNetwork(id, chainIdOverride); - return; - } - else - { - confirmationDialog.show(); - confirmationDialog.fullExpand(); - viewModel.track(Analytics.Action.WALLET_CONNECT_SESSION_REQUEST); - } - - if (confirmationDialog.isShowing() && - !viewModel.isActiveNetwork(chainId) && - !viewModel.isActiveNetwork(chainIdOverride)) - { - openChainSelection(); - } - } - - private void displaySessionRequestDetails(WCPeerMeta peer, long chainId, String displayIcon) - { - Glide.with(this) - .load(displayIcon) - .circleCrop() - .into(icon); - peerName.setText(peer.getName()); - textName.setText(peer.getName()); - peerUrl.setText(peer.getUrl()); - txCount.setText(R.string.empty); - chainName.setChainID(chainId); - chainIcon.setVisibility(View.VISIBLE); - chainIcon.bindData(chainId); - remotePeerMeta = peer; - } - - private void closeConfirmationDialog() - { - if (confirmationDialog != null) - { - if (confirmationDialog.isShowing()) - { // if already opened - confirmationDialog.forceDismiss(); - } - } - } - - private void onEthSign(Long id, WCEthereumSignMessage message, long chainId) - { - Signable signable = null; - lastId = id; - signData = message.getData(); - signType = message.getType(); - - switch (message.getType()) - { - case MESSAGE: - // see https://docs.walletconnect.org/json-rpc-api-methods/ethereum - // WalletConnect doesn't provide access to deprecated eth_sign - // Instead it uses sign_personal for both - // signable = new EthereumMessage(message.getData(), peerUrl.getText().toString(), id, SignMessageType.SIGN_MESSAGE); - // break; - // Drop through - case PERSONAL_MESSAGE: - signable = new EthereumMessage(message.getData(), peerUrl.getText().toString(), id, SignMessageType.SIGN_PERSONAL_MESSAGE); - doSignMessage(signable); - break; - case TYPED_MESSAGE: - signable = new EthereumTypedMessage(message.getData(), peerUrl.getText().toString(), id, new CryptoFunctions()); - if (signable.getChainId() != chainId) - { - showErrorDialogIncompatibleNetwork(signable.getCallbackId(), signable.getChainId(), chainId); - } - else - { - doSignMessage(signable); - } - break; - } - } - - private void showErrorDialogUnsupportedNetwork(long callbackId, long chainId) - { - if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) - { - runOnUiThread(() -> { - closeErrorDialog(); - dialog = new AWalletAlertDialog(this, AWalletAlertDialog.ERROR); - String message = getString(R.string.error_walletconnect_session_request_unsupported_network, String.valueOf(chainId)); - dialog.setMessage(message); - dialog.setButton(R.string.action_close, v -> { - dialog.dismiss(); - dismissed("", callbackId, false); - finish(); - }); - dialog.setCancelable(false); - dialog.show(); - - viewModel.trackError(Analytics.Error.WALLET_CONNECT, message); - }); - } - } - - private void showErrorDialogIncompatibleNetwork(long callbackId, long requestingChainId, long activeChainId) - { - if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) - { - runOnUiThread(() -> { - closeErrorDialog(); - dialog = new AWalletAlertDialog(this, AWalletAlertDialog.ERROR); - String message = EthereumNetworkBase.isChainSupported(requestingChainId) ? - getString(R.string.error_eip712_incompatible_network, - EthereumNetworkBase.getShortChainName(requestingChainId), - EthereumNetworkBase.getShortChainName(activeChainId)) : - getString(R.string.error_eip712_unsupported_network, String.valueOf(requestingChainId)); - dialog.setMessage(message); - dialog.setButton(R.string.action_cancel, v -> { - dialog.dismiss(); - dismissed("", callbackId, false); - }); - dialog.setCancelable(false); - dialog.show(); - - viewModel.trackError(Analytics.Error.WALLET_CONNECT, message); - }); - } - } - - private void onFailure(@NonNull Throwable throwable) - { - closeErrorDialog(); - dialog = new AWalletAlertDialog(this, AWalletAlertDialog.ERROR); - dialog.setTitle(R.string.title_dialog_error); - dialog.setMessage(throwable.getMessage()); - dialog.setButton(R.string.try_again, v -> onDefaultWallet(viewModel.getWallet())); - dialog.setSecondaryButton(R.string.action_cancel, v -> { - dialog.dismiss(); - killSession(); - finish(); - }); - dialog.setCancelable(false); - dialog.show(); - - AnalyticsProperties props = new AnalyticsProperties(); - props.put(Analytics.PROPS_ERROR_MESSAGE, throwable.getMessage()); - viewModel.track(Analytics.Action.WALLET_CONNECT_TRANSACTION_FAILED, props); - } - - private void doSignMessage(final Signable signable) - { - confirmationDialog = new ActionSheetSignDialog(this, this, signable); - confirmationDialog.show(); - - viewModel.track(Analytics.Action.WALLET_CONNECT_SIGN_MESSAGE_REQUEST); - } - - @Override - public void signingComplete(SignatureFromKey signature, Signable signable) - { - viewModel.recordSign(signable, getSessionId(), () -> { - viewModel.approveRequest(getApplication(), getSessionId(), signable.getCallbackId(), Numeric.toHexString(signature.signature)); - confirmationDialog.success(); - if (fromDappBrowser) - { - confirmationDialog.forceDismiss(); - switchToDappBrowser(); - } - requestId = 0; - lastId = 0; - signData = null; - updateSignCount(); - }); - } - - @Override - public void signingFailed(Throwable error, Signable message) - { - showErrorDialog(error.getMessage()); - confirmationDialog.dismiss(); - if (fromDappBrowser) switchToDappBrowser(); - requestId = 0; - lastId = 0; - signData = null; - } - - @Override - public WalletType getWalletType() - { - return viewModel.getWallet().type; - } - - private void onEthSignTransaction(Long id, WCEthereumTransaction transaction, long chainId) - { - lastId = id; - final Web3Transaction w3Tx = new Web3Transaction(transaction, id, SignType.SIGN_TX); - final ActionSheetDialog confDialog = generateTransactionRequest(w3Tx, chainId); - if (confDialog != null) - { - confirmationDialog = confDialog; - confirmationDialog.setSignOnly(); //sign transaction only - confirmationDialog.show(); - - viewModel.track(Analytics.Action.WALLET_CONNECT_SIGN_TRANSACTION_REQUEST); - } - } - - private void onEthSendTransaction(Long id, WCEthereumTransaction transaction, long chainId) - { - lastId = id; - final Web3Transaction w3Tx = new Web3Transaction(transaction, id, SignType.SEND_TX); - final ActionSheetDialog confDialog = generateTransactionRequest(w3Tx, chainId); - if (confDialog != null) - { - confirmationDialog = confDialog; - confirmationDialog.show(); - - viewModel.track(Analytics.Action.WALLET_CONNECT_SEND_TRANSACTION_REQUEST); - } - } - - private ActionSheetDialog generateTransactionRequest(Web3Transaction w3Tx, long chainId) - { - try - { - if ((confirmationDialog == null || !confirmationDialog.isShowing()) && - (w3Tx.recipient.equals(Address.EMPTY) && w3Tx.payload != null) // Constructor - || (!w3Tx.recipient.equals(Address.EMPTY) && (w3Tx.payload != null || w3Tx.value != null))) // Raw or Function TX - { - WCPeerMeta remotePeerData = viewModel.getRemotePeer(getSessionId()); - Token token = viewModel.getTokensService().getTokenOrBase(chainId, w3Tx.recipient.toString()); - final ActionSheetDialog confDialog = new ActionSheetDialog(this, w3Tx, token, "", - w3Tx.recipient.toString(), viewModel.getTokensService(), this); - confDialog.setURL(remotePeerData.getUrl()); - confDialog.setCanceledOnTouchOutside(false); - confDialog.waitForEstimate(); - - viewModel.calculateGasEstimate(viewModel.getWallet(), w3Tx, chainId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(confDialog::setGasEstimate, - Throwable::printStackTrace) - .isDisposed(); - - return confDialog; - } - } - catch (Exception e) - { - Timber.e(e); - } - - return null; - } - - private void killSession() - { - Timber.tag(TAG).d(": Terminate Session: %s", getSessionId()); - if (client != null && session != null && client.isConnected()) - { - viewModel.track(Analytics.Action.WALLET_CONNECT_SESSION_ENDED); - client.killSession(); - viewModel.disconnectSession(this, client.sessionId()); - awWalletConnectClient.updateNotification(); - handler.postDelayed(this::finish, 5000); - } - else - { - finish(); - } - } - - @Override - public void onPause() - { - super.onPause(); - broadcastManager.unregisterReceiver(walletConnectActionReceiver); - } - - @Override - public void onResume() - { - super.onResume(); - viewModel.track(Analytics.Navigation.WALLET_CONNECT_SESSION_DETAIL); - //see if the session is active - setupClient(getSessionId()); - startMessageCheck(); - } - - @Override - public void onDestroy() - { - super.onDestroy(); - if (viewModel != null) viewModel.onDestroy(); - } - - private void showTimeoutDialog() - { - if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) - { - runOnUiThread(() -> { - closeErrorDialog(); - dialog = new AWalletAlertDialog(this, AWalletAlertDialog.ERROR); - dialog.setTitle(R.string.title_dialog_error); - dialog.setMessage(R.string.walletconnect_timeout); - dialog.setButton(R.string.ok, v -> dialog.dismiss()); - dialog.setSecondaryButton(R.string.action_close, v -> { - dialog.dismiss(); - finish(); - }); - dialog.setCancelable(false); - dialog.show(); - - viewModel.track(Analytics.Action.WALLET_CONNECT_CONNECTION_TIMEOUT); - }); - } - } - - private void showErrorDialog(String message) - { - if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) - { - runOnUiThread(() -> { - closeErrorDialog(); - dialog = new AWalletAlertDialog(this, AWalletAlertDialog.ERROR); - dialog.setTitle(R.string.title_dialog_error); - dialog.setMessage(message); - dialog.setButton(R.string.try_again, v -> onDefaultWallet(viewModel.getWallet())); - dialog.setSecondaryButton(R.string.action_close, v -> { - dialog.dismiss(); - killSession(); - }); - dialog.setCancelable(false); - dialog.show(); - - viewModel.trackError(Analytics.Error.WALLET_CONNECT, message); - }); - } - } - - private void showErrorDialogCancel(String title, String message) - { - if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) - { - runOnUiThread(() -> { - closeErrorDialog(); - dialog = new AWalletAlertDialog(this, AWalletAlertDialog.ERROR); - dialog.setTitle(title); - dialog.setMessage(message); - dialog.setButton(R.string.action_cancel, v -> dialog.dismiss()); - dialog.setCancelable(false); - dialog.show(); - - viewModel.trackError(Analytics.Error.WALLET_CONNECT, message); - }); - } - } - - @Override - public void onBackPressed() - { - //TODO: If from phone browser then we should return a code that tells main activity to finish - if (fromDappBrowser) - { - switchToDappBrowser(); - } - else - { - //hand back to phone browser - finish(); - } - } - - @Override - public void finish() - { - super.finish(); - } - - private void endSessionDialog() - { - runOnUiThread(() -> { - closeErrorDialog(); - dialog = new AWalletAlertDialog(this, AWalletAlertDialog.ERROR); - dialog.setTitle(R.string.dialog_title_disconnect_session); - dialog.setButton(R.string.action_close, v -> { - dialog.dismiss(); - killSession(); - }); - dialog.setSecondaryButton(R.string.action_cancel, v -> dialog.dismiss()); - dialog.setCancelable(false); - dialog.show(); - }); - } - - private void showErrorDialogTerminate(String message) - { - if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) - { - runOnUiThread(() -> { - closeErrorDialog(); - dialog = new AWalletAlertDialog(this, AWalletAlertDialog.ERROR); - dialog.setTitle(R.string.title_dialog_error); - dialog.setMessage(message); - dialog.setButton(R.string.dialog_ok, v -> { - dialog.dismiss(); - finish(); - }); - dialog.setCancelable(false); - dialog.show(); - - viewModel.trackError(Analytics.Error.WALLET_CONNECT, message); - }); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) - { - if (item.getItemId() == android.R.id.home) - { - onBackPressed(); - } - return false; - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) - { - super.onActivityResult(requestCode, resultCode, data); - - if (requestCode >= SignTransactionDialog.REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS && requestCode <= SignTransactionDialog.REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS + 10) - { - if (confirmationDialog != null && confirmationDialog.isShowing()) - confirmationDialog.completeSignRequest(resultCode == RESULT_OK); - } - if (resultCode == RESULT_OK) - { - if (requestCode == C.REQUEST_TRANSACTION_CALLBACK) - { - handleTransactionCallback(resultCode, data); - } - } - else - { - showErrorDialogCancel(peerName.getText().toString(), getString(R.string.message_transaction_not_sent)); - if (requestCode == C.REQUEST_TRANSACTION_CALLBACK) - { - viewModel.rejectRequest(this, getSessionId(), lastId, getString(R.string.message_authentication_failed)); - } - else - { - viewModel.rejectRequest(this, getSessionId(), lastId, getString(R.string.message_reject_request)); - } - } - } - - //return from the openConfirmation above - public void handleTransactionCallback(int resultCode, Intent data) - { - if (data == null) return; - final Web3Transaction web3Tx = data.getParcelableExtra(C.EXTRA_WEB3TRANSACTION); - if (resultCode == RESULT_OK && web3Tx != null) - { - String hashData = data.getStringExtra(C.EXTRA_TRANSACTION_DATA); - viewModel.recordSignTransaction(getApplicationContext(), web3Tx, String.valueOf(viewModel.getChainId(getSessionId())), getSessionId()); - viewModel.approveRequest(this, getSessionId(), lastId, hashData); - } - else if (web3Tx != null) - { - showErrorDialogCancel(getString(R.string.title_wallet_connect), getString(R.string.message_transaction_not_sent)); - viewModel.rejectRequest(this, getSessionId(), lastId, getString(R.string.message_reject_request)); - } - - if (fromDappBrowser) - { - if (confirmationDialog != null) confirmationDialog.forceDismiss(); - switchToDappBrowser(); - } - } - - private void switchToDappBrowser() - { - Intent intent = new Intent(this, HomeActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - startActivity(intent); - } - - @Override - public void getAuthorisation(SignAuthenticationCallback callback) - { - viewModel.getAuthenticationForSignature(viewModel.getWallet(), this, callback); - } - - @Override - public void sendTransaction(Web3Transaction finalTx) - { - viewModel.requestSignature(finalTx, viewModel.getWallet(), viewModel.getChainId(getSessionId())); - } - - /** - * Complete Hardware sendTx/signTx - */ - @Override - public void completeSendTransaction(Web3Transaction tx, SignatureFromKey signature) - { - viewModel.sendTransaction(viewModel.getWallet(), viewModel.getChainId(getSessionId()), tx, signature); - } - - @Override - public void completeSignTransaction(Web3Transaction w3Tx, SignatureFromKey signature) - { - viewModel.signTransaction(viewModel.getChainId(getSessionId()), w3Tx, signature); - } - - public void txWritten(TransactionReturn txReturn) - { - viewModel.recordSignTransaction(getApplicationContext(), txReturn.tx, String.valueOf(viewModel.getChainId(getSessionId())), getSessionId()); - viewModel.approveRequest(getApplication(), getSessionId(), txReturn.tx.leafPosition, txReturn.hash); - confirmationDialog.transactionWritten(getString(R.string.dialog_title_sign_transaction)); - if (fromDappBrowser) switchToDappBrowser(); - confirmationDialog.transactionWritten(txReturn.hash); - requestId = 0; - updateSignCount(); - - viewModel.track(Analytics.Action.WALLET_CONNECT_TRANSACTION_SUCCESS); - } - - private void txSigned(TransactionReturn sigData) - { - if (sigData != null) - { - viewModel.recordSignTransaction(getApplicationContext(), sigData.tx, String.valueOf(viewModel.getChainId(getSessionId())), getSessionId()); - viewModel.approveRequest(getApplication(), getSessionId(), sigData.tx.leafPosition, sigData.hash); - confirmationDialog.transactionWritten(getString(R.string.dialog_title_sign_transaction)); - if (fromDappBrowser) - { - switchToDappBrowser(); - } - requestId = 0; - updateSignCount(); - } - } - - //Transaction failed to be sent - private void txError(TransactionReturn txError) - { - if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) - { - runOnUiThread(() -> { - closeErrorDialog(); - dialog = new AWalletAlertDialog(this, AWalletAlertDialog.ERROR); - dialog.setTitle(R.string.invalid_walletconnect_session); - dialog.setTitle(R.string.error_transaction_failed); - dialog.setMessage(txError.throwable.getMessage()); - dialog.setButtonText(R.string.button_ok); - dialog.setButtonListener(v -> { - dialog.dismiss(); - if (fromDappBrowser) switchToDappBrowser(); - }); - dialog.show(); - - AnalyticsProperties props = new AnalyticsProperties(); - props.put(Analytics.PROPS_ERROR_MESSAGE, txError.throwable.getMessage()); - viewModel.track(Analytics.Action.WALLET_CONNECT_TRANSACTION_FAILED, props); - }); - } - //TODO: Report error fail to WalletConnect - } - - @Override - public void dismissed(String txHash, long callbackId, boolean actionCompleted) - { - //actionsheet dismissed - if action not completed then user cancelled - if (!actionCompleted) - { - viewModel.rejectRequest(this, getSessionId(), callbackId, getString(R.string.message_reject_request)); - viewModel.track(Analytics.Action.WALLET_CONNECT_TRANSACTION_CANCELLED); - } - - if (fromDappBrowser) switchToDappBrowser(); - requestId = 0; - confirmationDialog = null; - } - - @Override - public void notifyConfirm(String mode) - { - AnalyticsProperties props = new AnalyticsProperties(); - props.put(Analytics.PROPS_ACTION_SHEET_MODE, mode); - props.put(Analytics.PROPS_ACTION_SHEET_SOURCE, ActionSheetSource.WALLET_CONNECT.getValue()); - viewModel.track(Analytics.Action.ACTION_SHEET_COMPLETED, props); - } - - @Override - public ActivityResultLauncher gasSelectLauncher() - { - return getGasSettings; - } - - @Override - public void signTransaction(Web3Transaction tx) - { - viewModel.requestSignatureOnly(tx, viewModel.getWallet(), viewModel.getChainId(getSessionId())); - } - - @Override - public void notifyWalletConnectApproval(long selectedChain) - { - client.approveSession(Collections.singletonList(viewModel.getWallet().address), selectedChain); - //update client in service - viewModel.putClient(WalletConnectActivity.this, getSessionId(), client); - viewModel.createNewSession(getSessionId(), client.getPeerId(), client.getRemotePeerId(), - new Gson().toJson(session), new Gson().toJson(remotePeerMeta), selectedChain); - chainName.setChainID(selectedChain); - chainIcon.setVisibility(View.VISIBLE); - chainIcon.bindData(selectedChain); - progressBar.setVisibility(View.GONE); - functionBar.setVisibility(View.VISIBLE); - infoLayout.setVisibility(View.VISIBLE); - chainIdOverride = selectedChain; - setupClient(getSessionId()); //should populate this activity - viewModel.track(Analytics.Action.WALLET_CONNECT_SESSION_APPROVED); - if (fromDappBrowser) - { - //switch back to dappBrowser - switchToDappBrowser(); - } - } - - @Override - public GasService getGasService() - { - return viewModel.getGasService(); - } - - @Override - public void denyWalletConnect() - { - client.rejectSession(getString(R.string.message_reject_request)); - viewModel.track(Analytics.Action.WALLET_CONNECT_SESSION_REJECTED); - finish(); - } - - @Override - public void openChainSelection() - { - ActionSheetCallback.super.openChainSelection(); - Intent intent = new Intent(WalletConnectActivity.this, NetworkChooserActivity.class); - intent.putExtra(C.EXTRA_SINGLE_ITEM, true); - intent.putExtra(C.EXTRA_CHAIN_ID, chainIdOverride); - getNetwork.launch(intent); - } - - private void showSwitchChainDialog() - { - try - { - Token baseToken = viewModel.getTokenService().getTokenOrBase(switchChainId, viewModel.defaultWallet().getValue().address); - NetworkInfo newNetwork = EthereumNetworkBase.getNetworkInfo(switchChainId); - NetworkInfo activeNetwork = EthereumNetworkBase.getNetworkInfo(client.chainIdVal()); - if (confirmationDialog != null && confirmationDialog.isShowing()) - return; - confirmationDialog = new ActionSheetDialog(this, this, R.string.switch_chain_request, R.string.switch_and_reload, - switchChainDialogCallbackId, baseToken, activeNetwork, newNetwork); - confirmationDialog.setOnDismissListener(dialog -> { - viewModel.approveSwitchEthChain(WalletConnectActivity.this, switchChainRequestId, currentSessionId, switchChainId, false, chainAvailable); - confirmationDialog.setOnDismissListener(null); // remove from here as dialog is multi-purpose - confirmationDialog = null; - }); - confirmationDialog.setCanceledOnTouchOutside(false); - confirmationDialog.show(); - confirmationDialog.fullExpand(); - - viewModel.track(Analytics.Action.WALLET_CONNECT_SWITCH_NETWORK_REQUEST); - } - catch (Exception e) - { - Timber.e(e); - } - } - - private void onSwitchChainRequest(Intent intent) - { - name = intent.getStringExtra(C.EXTRA_NAME); - switchChainRequestId = intent.getLongExtra(C.EXTRA_WC_REQUEST_ID, -1); - switchChainId = intent.getLongExtra(C.EXTRA_CHAIN_ID, -1); - currentSessionId = intent.getStringExtra(C.EXTRA_SESSION_ID); - Timber.tag(TAG).d("MSG: SWITCH CHAIN: name: %s, chainId: %s", name, switchChainId); - - if (currentSessionId == null || !session.getTopic().equals(currentSessionId)) - { - Timber.tag(TAG).d("Wrong session"); - return; - } - if (switchChainId == -1 || requestId == -1) - { - Timber.tag(TAG).d("Cant find data"); - return; - } - chainAvailable = EthereumNetworkBase.getNetworkInfo(switchChainId) != null; - // reject with error message as the chain is not added - if (!chainAvailable) - { - viewModel.approveSwitchEthChain(WalletConnectActivity.this, requestId, currentSessionId, switchChainId, false, false); - } - else - { - showSwitchChainDialog(); - } - } - - private void onAddChainRequest(Intent intent) - { - long requestId = intent.getLongExtra(C.EXTRA_WC_REQUEST_ID, -1); - String currentSessionId = intent.getStringExtra(C.EXTRA_SESSION_ID); - WalletAddEthereumChainObject chainObject = intent.getParcelableExtra(C.EXTRA_CHAIN_OBJ); - if (chainObject != null) - { - // showing dialog because chain is not added - addEthereumChainPrompt = new AddEthereumChainPrompt( - this, - chainObject, - chainObject1 -> { - this.addEthereumChainPrompt.setOnDismissListener(null); - this.addEthereumChainPrompt.dismiss(); - viewModel.approveAddEthereumChain( - WalletConnectActivity.this, - requestId, - currentSessionId, - chainObject, - true - ); - viewModel.updateSession(currentSessionId, chainObject.getChainId()); - displaySessionStatus(currentSessionId); - } - ); - - addEthereumChainPrompt.setOnDismissListener(dialog -> { - viewModel.approveAddEthereumChain( - WalletConnectActivity.this, - requestId, - currentSessionId, - chainObject, - false - ); - }); - addEthereumChainPrompt.show(); - } - else - { - viewModel.approveAddEthereumChain( - WalletConnectActivity.this, - requestId, - currentSessionId, - chainObject, - false - ); - } - } - - @Override - public void buttonClick(long callbackId, Token baseToken) - { - if (callbackId == switchChainDialogCallbackId && confirmationDialog != null) - { - confirmationDialog.setOnDismissListener(null); - confirmationDialog.dismiss(); - viewModel.approveSwitchEthChain(WalletConnectActivity.this, switchChainRequestId, currentSessionId, switchChainId, true, chainAvailable); - viewModel.updateSession(currentSessionId, switchChainId); - displaySessionStatus(session.getTopic()); - } - } - - private void updateSignCount() - { - ArrayList recordList = viewModel.getSignRecords(getSessionId()); - txCount.setText(String.valueOf(recordList.size())); - if (recordList.size() > 0) - { - txCountLayout.setOnClickListener(v -> { - Intent intent = new Intent(getApplication(), SignDetailActivity.class); - intent.putParcelableArrayListExtra(C.EXTRA_STATE, recordList); - intent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); - startActivity(intent); - }); - } - } -} diff --git a/app/src/main/java/com/alphawallet/app/ui/WalletConnectSessionActivity.java b/app/src/main/java/com/alphawallet/app/ui/WalletConnectSessionActivity.java index f1bb6af005..9506215d4f 100644 --- a/app/src/main/java/com/alphawallet/app/ui/WalletConnectSessionActivity.java +++ b/app/src/main/java/com/alphawallet/app/ui/WalletConnectSessionActivity.java @@ -192,12 +192,12 @@ private void openDeleteMenu(View v) popupMenu.setOnMenuItemClickListener(item -> { if (item.getItemId() == R.id.action_delete_empty) { - viewModel.removeSessionsWithoutSignRecords(this); + //viewModel.removeSessionsWithoutSignRecords(this); return true; } else if (item.getItemId() == R.id.action_delete_all) { - viewModel.removeInactiveSessions(this); + //viewModel.removeInactiveSessions(this); return true; } return false; @@ -207,16 +207,7 @@ else if (item.getItemId() == R.id.action_delete_all) private void setupClient(final WalletConnectSessionItem session, final CustomAdapter.CustomViewHolder holder) { - if (session.wcVersion == 1) - { - viewModel.getClient(this, session.sessionId, client -> handler.post(() -> { - setStatusIconActive(holder, (client != null && client.isConnected())); - })); - } - else - { - setStatusIconActive(holder, (session.expiryTime > System.currentTimeMillis())); - } + setStatusIconActive(holder, (session.expiryTime > System.currentTimeMillis())); } private void setStatusIconActive(final CustomAdapter.CustomViewHolder holder, boolean active) @@ -244,7 +235,7 @@ private void dialogConfirmDelete(WalletConnectSessionItem session) @Override public void onSessionDisconnected() { - runOnUiThread(() -> awWalletConnectClient.updateNotification()); + //runOnUiThread(() -> awWalletConnectClient.updateNotification()); } })); cDialog.setSecondaryButtonText(R.string.action_cancel); @@ -278,7 +269,7 @@ public CustomAdapter.CustomViewHolder onCreateViewHolder(@NonNull ViewGroup pare View itemView = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_wc_session, parent, false); - return new CustomAdapter.CustomViewHolder(itemView); + return new CustomViewHolder(itemView); } @Override @@ -326,7 +317,7 @@ public void updateList(List list) notifyDataSetChanged(); } - class CustomViewHolder extends RecyclerView.ViewHolder + static class CustomViewHolder extends RecyclerView.ViewHolder { final ImageView icon; final ImageView statusIcon; @@ -352,17 +343,9 @@ class CustomViewHolder extends RecyclerView.ViewHolder public static Intent newIntent(Context context, WalletConnectSessionItem session) { - Intent intent; - if (session instanceof WalletConnectV2SessionItem) - { - intent = new Intent(context, WalletConnectV2Activity.class); - intent.putExtra("session", (WalletConnectV2SessionItem) session); - } - else - { - intent = new Intent(context, WalletConnectActivity.class); - intent.putExtra("session", session.sessionId); - } + Intent intent = new Intent(context, WalletConnectV2Activity.class); + intent.putExtra("session", (WalletConnectV2SessionItem) session); + intent.addFlags(FLAG_ACTIVITY_NEW_TASK); return intent; } diff --git a/app/src/main/java/com/alphawallet/app/ui/WalletFragment.java b/app/src/main/java/com/alphawallet/app/ui/WalletFragment.java index 29ed85bd21..520238d1f3 100644 --- a/app/src/main/java/com/alphawallet/app/ui/WalletFragment.java +++ b/app/src/main/java/com/alphawallet/app/ui/WalletFragment.java @@ -386,7 +386,6 @@ private void refreshList() adapter.clear(); viewModel.prepare(); viewModel.notifyRefresh(); - awWalletConnectClient.updateNotification(); }); } @@ -407,6 +406,7 @@ public void comeIntoFocus() viewModel.startUpdateListener(); viewModel.getTokensService().startUpdateCycleIfRequired(); } + checkWalletConnect(); } @Override @@ -566,6 +566,7 @@ public void onResume() if (viewModel == null) { requireActivity().recreate(); + return; } else { @@ -581,6 +582,16 @@ public void onResume() viewModel.startUpdateListener(); viewModel.getTokensService().startUpdateCycleIfRequired(); } + + checkWalletConnect(); + } + + private void checkWalletConnect() + { + if (adapter != null) + { + adapter.checkWalletConnect(); + } } private void onTokens(TokenCardMeta[] tokens) @@ -595,7 +606,7 @@ private void onTokens(TokenCardMeta[] tokens) if (currentTabPos.equals(TokenFilter.ALL)) { - awWalletConnectClient.updateNotification(); + checkWalletConnect(); } else { @@ -807,6 +818,19 @@ public void onSearchClicked() //startActivity(intent); } + @Override + public void onWCClicked() + { + Intent intent = awWalletConnectClient.getSessionIntent(getContext()); + startActivity(intent); + } + + @Override + public boolean hasWCSession() + { + return awWalletConnectClient != null && awWalletConnectClient.hasWalletConnectSessions(); + } + @Override public void onSwitchClicked() { diff --git a/app/src/main/java/com/alphawallet/app/ui/WalletsActivity.java b/app/src/main/java/com/alphawallet/app/ui/WalletsActivity.java index 100cd17668..00e8638361 100644 --- a/app/src/main/java/com/alphawallet/app/ui/WalletsActivity.java +++ b/app/src/main/java/com/alphawallet/app/ui/WalletsActivity.java @@ -35,7 +35,6 @@ import com.alphawallet.app.repository.EthereumNetworkRepository; import com.alphawallet.app.repository.PreferenceRepositoryType; import com.alphawallet.app.service.KeyService; -import com.alphawallet.app.service.WalletConnectService; import com.alphawallet.app.ui.widget.adapter.WalletsSummaryAdapter; import com.alphawallet.app.viewmodel.WalletsViewModel; import com.alphawallet.app.widget.AWalletAlertDialog; @@ -431,10 +430,10 @@ private void updateCurrentWallet(Wallet wallet) viewModel.stopUpdates(); - Intent bIntent = new Intent(this, WalletConnectService.class); + /*Intent bIntent = new Intent(this, WalletConnectService.class); bIntent.setAction(String.valueOf(WalletConnectActions.DISCONNECT.ordinal())); bIntent.putExtra("wallet", selectedWallet); - startService(bIntent); + startService(bIntent);*/ selectedWallet = wallet; } diff --git a/app/src/main/java/com/alphawallet/app/ui/widget/TokensAdapterCallback.java b/app/src/main/java/com/alphawallet/app/ui/widget/TokensAdapterCallback.java index f95b76b696..a858e0d8b4 100644 --- a/app/src/main/java/com/alphawallet/app/ui/widget/TokensAdapterCallback.java +++ b/app/src/main/java/com/alphawallet/app/ui/widget/TokensAdapterCallback.java @@ -14,5 +14,7 @@ public interface TokensAdapterCallback default void reloadTokens() { }; default void onBuyToken() { } default void onSearchClicked() { }; - default void onSwitchClicked() {}; + default void onSwitchClicked() { }; + default void onWCClicked() { }; + default boolean hasWCSession() { return false; }; } diff --git a/app/src/main/java/com/alphawallet/app/ui/widget/adapter/ActivityAdapter.java b/app/src/main/java/com/alphawallet/app/ui/widget/adapter/ActivityAdapter.java index 919e7da648..bb44f6fcf3 100644 --- a/app/src/main/java/com/alphawallet/app/ui/widget/adapter/ActivityAdapter.java +++ b/app/src/main/java/com/alphawallet/app/ui/widget/adapter/ActivityAdapter.java @@ -116,8 +116,7 @@ public ActivityAdapter(TokensService service, FetchTransactionsInteract fetchTra public BinderViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { switch (viewType) { case TransactionHolder.VIEW_TYPE: - return new TransactionHolder(parent, tokensService, fetchTransactionsInteract, - assetService); + return new TransactionHolder(parent, tokensService, fetchTransactionsInteract); case EventHolder.VIEW_TYPE: return new EventHolder(parent, tokensService, fetchTransactionsInteract, assetService, this); diff --git a/app/src/main/java/com/alphawallet/app/ui/widget/adapter/TokensAdapter.java b/app/src/main/java/com/alphawallet/app/ui/widget/adapter/TokensAdapter.java index 183a849b5d..391544f0a0 100644 --- a/app/src/main/java/com/alphawallet/app/ui/widget/adapter/TokensAdapter.java +++ b/app/src/main/java/com/alphawallet/app/ui/widget/adapter/TokensAdapter.java @@ -13,11 +13,9 @@ import com.alphawallet.app.entity.CustomViewSettings; import com.alphawallet.app.entity.TokenFilter; import com.alphawallet.app.entity.tokendata.TokenGroup; -import com.alphawallet.app.entity.tokens.Attestation; import com.alphawallet.app.entity.tokens.Token; import com.alphawallet.app.entity.tokens.TokenCardMeta; import com.alphawallet.app.entity.walletconnect.WalletConnectSessionItem; -import com.alphawallet.app.repository.TokensRealmSource; import com.alphawallet.app.service.AssetDefinitionService; import com.alphawallet.app.service.TokensService; import com.alphawallet.app.ui.widget.TokensAdapterCallback; @@ -169,7 +167,7 @@ public BinderViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int vie break; case SearchTokensHolder.VIEW_TYPE: - holder = new SearchTokensHolder(R.layout.layout_manage_token_search, parent, tokensAdapterCallback::onSearchClicked); + holder = new SearchTokensHolder(R.layout.layout_manage_token_search, parent, tokensAdapterCallback); break; case WarningHolder.VIEW_TYPE: @@ -301,11 +299,11 @@ public void updateToken(TokenCardMeta token) } else { - SortedItem headerItem = new HeaderItem(token.group); + SortedItem headerItem = new HeaderItem(token.group); items.add(tsi); items.add(headerItem); - SortedItem chainItem = new ChainItem(token.getChain(), token.group); + SortedItem chainItem = new ChainItem(token.getChain(), token.group); if (doesNotExist(chainItem)) { items.add(chainItem); @@ -318,12 +316,12 @@ public void updateToken(TokenCardMeta token) } } - private boolean doesNotExist(SortedItem token) + private boolean doesNotExist(SortedItem token) { return findItem(token) == -1; } - private int findItem(SortedItem tsi) + private int findItem(SortedItem tsi) { for (int i = 0; i < items.size(); i++) { @@ -339,9 +337,8 @@ private void removeMatchingTokenDifferentWeight(TokenCardMeta token) { for (int i = 0; i < items.size(); i++) { - if (items.get(i) instanceof TokenSortedItem) + if (items.get(i) instanceof TokenSortedItem tsi) { - TokenSortedItem tsi = (TokenSortedItem) items.get(i); if (tsi.value.equals(token)) { if (tsi.value.getNameWeight() != token.getNameWeight()) @@ -523,9 +520,8 @@ private void filterAdapterItems() for (int i = 0; i < items.size(); i++) { Object si = items.get(i); - if (si instanceof TokenSortedItem) + if (si instanceof TokenSortedItem tsi) { - TokenSortedItem tsi = (TokenSortedItem) si; if (canDisplayToken(tsi.value)) { filterTokens.add(tsi.value); @@ -567,9 +563,8 @@ public int getScrollPosition() for (int i = 0; i < items.size(); i++) { Object si = items.get(i); - if (si instanceof TokenSortedItem) + if (si instanceof TokenSortedItem tsi) { - TokenSortedItem tsi = (TokenSortedItem) si; TokenCardMeta token = tsi.value; if (scrollToken.equals(token)) { @@ -654,4 +649,26 @@ public void addToken(SortedItem token) { items.add(token); } + + public void checkWalletConnect() + { + //activate WC logo in search bar if we have active WC sessions + for (int i = 0; i < items.size(); i++) + { + Object si = items.get(i); + if (si instanceof ManageTokensSearchItem manageTokensSearchItem && manageTokensSearchItem.view instanceof SearchTokensHolder sth) + { + if (tokensAdapterCallback.hasWCSession()) + { + sth.enableWalletConnect(); + } + else + { + sth.hideWalletConnect(); + } + + break; + } + } + } } diff --git a/app/src/main/java/com/alphawallet/app/ui/widget/adapter/TraitsAdapter.java b/app/src/main/java/com/alphawallet/app/ui/widget/adapter/TraitsAdapter.java index f720cd0ebe..1fbd98e3c9 100644 --- a/app/src/main/java/com/alphawallet/app/ui/widget/adapter/TraitsAdapter.java +++ b/app/src/main/java/com/alphawallet/app/ui/widget/adapter/TraitsAdapter.java @@ -70,7 +70,14 @@ public void onBindViewHolder(@NonNull TraitsAdapter.ViewHolder viewHolder, int i @Override public int getItemCount() { - return traitList.size(); + if (traitList != null) + { + return traitList.size(); + } + else + { + return 0; + } } static class ViewHolder extends RecyclerView.ViewHolder { diff --git a/app/src/main/java/com/alphawallet/app/ui/widget/entity/IconItem.java b/app/src/main/java/com/alphawallet/app/ui/widget/entity/IconItem.java index dbff547845..09f64baaed 100644 --- a/app/src/main/java/com/alphawallet/app/ui/widget/entity/IconItem.java +++ b/app/src/main/java/com/alphawallet/app/ui/widget/entity/IconItem.java @@ -7,46 +7,12 @@ public class IconItem { private final String url; - private final UseIcon useText; - - private final static Map iconLoadType = new ConcurrentHashMap<>(); public IconItem(String url, long chainId, String correctedAddress) { this.url = url; - this.useText = getLoadType(chainId, correctedAddress); - } - - private UseIcon getLoadType(long chainId, String correctedAddress) - { - String key = databaseKey(chainId, correctedAddress); - return iconLoadType.containsKey(key) - ? (iconLoadType.get(key) ? UseIcon.SECONDARY : UseIcon.NO_ICON) - : UseIcon.PRIMARY; } public String getUrl() { return url; } - - public boolean useTextSymbol() { - return useText == UseIcon.NO_ICON; - } - - public boolean usePrimary() { - return useText == UseIcon.PRIMARY; - } - - //Use Secondary icon - public static void secondaryFound(long chainId, String address) - { - iconLoadType.put(databaseKey(chainId, address.toLowerCase()), true); - } - - /** - * Resets the failed icon fetch checking - try again to load failed icons - */ - public static void resetCheck() - { - iconLoadType.clear(); - } } diff --git a/app/src/main/java/com/alphawallet/app/ui/widget/holder/EventHolder.java b/app/src/main/java/com/alphawallet/app/ui/widget/holder/EventHolder.java index c020ecc27c..440f05f398 100644 --- a/app/src/main/java/com/alphawallet/app/ui/widget/holder/EventHolder.java +++ b/app/src/main/java/com/alphawallet/app/ui/widget/holder/EventHolder.java @@ -93,7 +93,7 @@ public void bind(@Nullable EventMeta data, @NonNull Bundle addition) if (token == null) token = tokensService.getToken(data.chainId, walletAddress); String sym = token.getShortSymbol(); - tokenIcon.bindData(token, assetDefinition); + tokenIcon.bindData(token); String itemView = null; TokenDefinition td = assetDefinition.getAssetDefinition(token); diff --git a/app/src/main/java/com/alphawallet/app/ui/widget/holder/SearchTokensHolder.java b/app/src/main/java/com/alphawallet/app/ui/widget/holder/SearchTokensHolder.java index 65ce0de7de..5545360c2b 100644 --- a/app/src/main/java/com/alphawallet/app/ui/widget/holder/SearchTokensHolder.java +++ b/app/src/main/java/com/alphawallet/app/ui/widget/holder/SearchTokensHolder.java @@ -4,11 +4,13 @@ import android.view.View; import android.view.ViewGroup; import android.widget.EditText; +import android.widget.ImageView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.alphawallet.app.R; +import com.alphawallet.app.ui.widget.TokensAdapterCallback; import com.alphawallet.app.ui.widget.entity.ManageTokensData; public class SearchTokensHolder extends BinderViewHolder { @@ -20,7 +22,9 @@ public interface SearchHandler { final EditText editSearch; final SearchHandler searchHandler; + final SearchHandler onWCClicked; final View searchTokenClick; + final ImageView walletConnect; String wallet; @Override @@ -38,11 +42,32 @@ public void bind(@Nullable ManageTokensData data, @NonNull Bundle addition) { }); } - public SearchTokensHolder(int res_id, ViewGroup parent, SearchHandler handler) { + public SearchTokensHolder(int res_id, ViewGroup parent, TokensAdapterCallback tCallback) { super(res_id, parent); this.editSearch = findViewById(R.id.edit_search); - this.searchHandler = handler; + this.searchHandler = tCallback::onSearchClicked; this.searchTokenClick = findViewById(R.id.click_layer); + this.walletConnect = findViewById(R.id.icon_wc_active); this.wallet = null; + this.onWCClicked = tCallback::onWCClicked; + + if (tCallback.hasWCSession()) + { + enableWalletConnect(); + } + } + + public void enableWalletConnect() + { + walletConnect.setVisibility(View.VISIBLE); + walletConnect.setOnClickListener(v -> { + if (onWCClicked != null) onWCClicked.onFocus(); + }); + } + + public void hideWalletConnect() + { + walletConnect.setVisibility(View.GONE); + walletConnect.setOnClickListener(null); } } diff --git a/app/src/main/java/com/alphawallet/app/ui/widget/holder/TokenGridHolder.java b/app/src/main/java/com/alphawallet/app/ui/widget/holder/TokenGridHolder.java index f636feffea..4cbd1626b0 100644 --- a/app/src/main/java/com/alphawallet/app/ui/widget/holder/TokenGridHolder.java +++ b/app/src/main/java/com/alphawallet/app/ui/widget/holder/TokenGridHolder.java @@ -52,7 +52,7 @@ public void bind(@Nullable TokenCardMeta tcm, @NonNull Bundle addition) { Token token = tokensService.getToken(tcm.getChain(), tcm.getAddress()); if (token == null) return; //TODO: Generate placeholder - imageIcon.bindData(token, assetDefinition); + imageIcon.bindData(token); name.setText(token.getName(assetDefinition, token.balance.intValue())); count.setText(getString(R.string.token_count, token.balance.intValue())); diff --git a/app/src/main/java/com/alphawallet/app/ui/widget/holder/TokenHolder.java b/app/src/main/java/com/alphawallet/app/ui/widget/holder/TokenHolder.java index 124ecc212d..a5de438022 100644 --- a/app/src/main/java/com/alphawallet/app/ui/widget/holder/TokenHolder.java +++ b/app/src/main/java/com/alphawallet/app/ui/widget/holder/TokenHolder.java @@ -155,7 +155,7 @@ else if (data.getNameWeight() < 1000L && !token.isEthereum()) balanceCoin.setText(getString(R.string.valueSymbol, coinBalance, symbol)); } - tokenIcon.bindData(token, assetDefinition); + tokenIcon.bindData(token); if (!token.isEthereum()) { tokenIcon.setChainIcon(token.tokenInfo.chainId); //Add in when we upgrade the design diff --git a/app/src/main/java/com/alphawallet/app/ui/widget/holder/TokenListHolder.java b/app/src/main/java/com/alphawallet/app/ui/widget/holder/TokenListHolder.java index 720b064dde..fe90e42a3c 100644 --- a/app/src/main/java/com/alphawallet/app/ui/widget/holder/TokenListHolder.java +++ b/app/src/main/java/com/alphawallet/app/ui/widget/holder/TokenListHolder.java @@ -69,7 +69,7 @@ public void bind(@Nullable TokenCardMeta data, @NonNull Bundle addition) switchEnabled.setOnCheckedChangeListener(null); switchEnabled.setChecked(data.isEnabled); switchEnabled.setOnCheckedChangeListener(this); - tokenIcon.bindData(token, assetDefinition); + tokenIcon.bindData(token); if (data.isEnabled) { @@ -109,4 +109,4 @@ public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) onTokenClickListener.onTokenClick(token, position, isChecked); } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/alphawallet/app/ui/widget/holder/TransactionHolder.java b/app/src/main/java/com/alphawallet/app/ui/widget/holder/TransactionHolder.java index 579f0fb451..21562a2536 100644 --- a/app/src/main/java/com/alphawallet/app/ui/widget/holder/TransactionHolder.java +++ b/app/src/main/java/com/alphawallet/app/ui/widget/holder/TransactionHolder.java @@ -44,12 +44,11 @@ public class TransactionHolder extends BinderViewHolder impleme private final TextView supplemental; private final TokensService tokensService; private final FetchTransactionsInteract transactionsInteract; - private final AssetDefinitionService assetService; private Transaction transaction; private String defaultAddress; - public TransactionHolder(ViewGroup parent, TokensService service, FetchTransactionsInteract interact, AssetDefinitionService svs) + public TransactionHolder(ViewGroup parent, TokensService service, FetchTransactionsInteract interact) { super(R.layout.item_transaction, parent); date = findViewById(R.id.text_tx_time); @@ -60,7 +59,6 @@ public TransactionHolder(ViewGroup parent, TokensService service, FetchTransacti supplemental = findViewById(R.id.supplimental); tokensService = service; transactionsInteract = interact; - assetService = svs; itemView.setOnClickListener(this); } @@ -97,7 +95,7 @@ public void bind(@Nullable TransactionMeta data, @NonNull Bundle addition) setTokenDetailName(token); //set colours and up/down arrow - tokenIcon.bindData(token, assetService); + tokenIcon.bindData(token); tokenIcon.setStatusIcon(token.getTxStatus(transaction)); tokenIcon.setChainIcon(token.tokenInfo.chainId); diff --git a/app/src/main/java/com/alphawallet/app/ui/widget/holder/TransferHolder.java b/app/src/main/java/com/alphawallet/app/ui/widget/holder/TransferHolder.java index 40b99b0fbf..07aa2d8916 100644 --- a/app/src/main/java/com/alphawallet/app/ui/widget/holder/TransferHolder.java +++ b/app/src/main/java/com/alphawallet/app/ui/widget/holder/TransferHolder.java @@ -98,7 +98,7 @@ public void bind(@Nullable TokenTransferData data, @NonNull Bundle addition) disposable.dispose(); } - tokenIcon.bindData(token, assetDefinition); + tokenIcon.bindData(token); //We haven't yet fetched the underlying transaction. Fetch and display if (tx == null) diff --git a/app/src/main/java/com/alphawallet/app/util/Utils.java b/app/src/main/java/com/alphawallet/app/util/Utils.java index 186ac955d9..1a40ca3448 100644 --- a/app/src/main/java/com/alphawallet/app/util/Utils.java +++ b/app/src/main/java/com/alphawallet/app/util/Utils.java @@ -96,7 +96,7 @@ public class Utils private static final String CHAIN_REPO_ADDRESS_TOKEN = "[CHAIN]"; private static final String TOKEN_LOGO = "/logo.png"; public static final String ALPHAWALLET_REPO_NAME = "https://raw.githubusercontent.com/alphawallet/iconassets/master/"; - private static final String TRUST_ICON_REPO_BASE = "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/"; + public static final String TRUST_ICON_REPO_BASE = "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/"; private static final String TRUST_ICON_REPO = TRUST_ICON_REPO_BASE + CHAIN_REPO_ADDRESS_TOKEN + "/assets/" + ICON_REPO_ADDRESS_TOKEN + TOKEN_LOGO; private static final String ALPHAWALLET_ICON_REPO = ALPHAWALLET_REPO_NAME + ICON_REPO_ADDRESS_TOKEN + TOKEN_LOGO; private static final String ATTESTATION_PREFIX = "#attestation="; diff --git a/app/src/main/java/com/alphawallet/app/util/ens/AWEnsResolver.java b/app/src/main/java/com/alphawallet/app/util/ens/AWEnsResolver.java index 26965a7da3..612e6354bc 100644 --- a/app/src/main/java/com/alphawallet/app/util/ens/AWEnsResolver.java +++ b/app/src/main/java/com/alphawallet/app/util/ens/AWEnsResolver.java @@ -7,6 +7,7 @@ import com.alphawallet.app.C; import com.alphawallet.app.entity.UnableToResolveENS; +import com.alphawallet.app.entity.nftassets.NFTAsset; import com.alphawallet.app.service.OpenSeaService; import com.alphawallet.app.util.Utils; import com.alphawallet.app.web3j.ens.EnsResolutionException; @@ -179,11 +180,11 @@ private String getEip155Url(String locator) String tokenId = matcher.group(8); String asset = new OpenSeaService().fetchAsset(chainId, tokenAddress, tokenId); - JSONObject assetObj = new JSONObject(asset); - String url = assetObj.getString(OPENSEA_IMAGE_PREVIEW); + NFTAsset nftAsset = new NFTAsset(asset); + String url = nftAsset.getThumbnail(); if (!TextUtils.isEmpty(url) && url.endsWith(".svg")) { - String original = assetObj.getString(OPENSEA_IMAGE_ORIGINAL); + String original = nftAsset.getImage(); if (!TextUtils.isEmpty(original)) url = original; } return url; diff --git a/app/src/main/java/com/alphawallet/app/viewmodel/DappBrowserViewModel.java b/app/src/main/java/com/alphawallet/app/viewmodel/DappBrowserViewModel.java index 49f0598d36..6cc8585058 100644 --- a/app/src/main/java/com/alphawallet/app/viewmodel/DappBrowserViewModel.java +++ b/app/src/main/java/com/alphawallet/app/viewmodel/DappBrowserViewModel.java @@ -43,7 +43,6 @@ import com.alphawallet.app.ui.MyAddressActivity; import com.alphawallet.app.ui.QRScanning.QRScannerActivity; import com.alphawallet.app.ui.SendActivity; -import com.alphawallet.app.ui.WalletConnectActivity; import com.alphawallet.app.ui.WalletConnectV2Activity; import com.alphawallet.app.util.DappBrowserUtils; import com.alphawallet.app.walletconnect.util.WalletConnectHelper; @@ -366,16 +365,7 @@ public void stopBalanceUpdate() public void handleWalletConnect(Context context, String url, NetworkInfo activeNetwork) { - Intent intent; - if (WalletConnectHelper.isWalletConnectV1(url)) - { - intent = getIntentOfWalletConnectV1(context, url, activeNetwork); - } - else - { - intent = getIntentOfWalletConnectV2(context, url); - } - + Intent intent = getIntentOfWalletConnectV2(context, url); context.startActivity(intent); } @@ -387,17 +377,6 @@ private Intent getIntentOfWalletConnectV2(Context context, String url) return intent; } - @NonNull - private Intent getIntentOfWalletConnectV1(Context context, String url, NetworkInfo activeNetwork) - { - String importPassData = WalletConnectActivity.WC_LOCAL_PREFIX + url; - Intent intent = new Intent(context, WalletConnectActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - intent.putExtra(C.EXTRA_CHAIN_ID, activeNetwork.chainId); - intent.putExtra("qrCode", importPassData); - return intent; - } - public TokensService getTokenService() { return tokensService; diff --git a/app/src/main/java/com/alphawallet/app/viewmodel/HomeViewModel.java b/app/src/main/java/com/alphawallet/app/viewmodel/HomeViewModel.java index 607e1afced..12f55ea286 100644 --- a/app/src/main/java/com/alphawallet/app/viewmodel/HomeViewModel.java +++ b/app/src/main/java/com/alphawallet/app/viewmodel/HomeViewModel.java @@ -1,21 +1,16 @@ package com.alphawallet.app.viewmodel; -import static com.alphawallet.app.viewmodel.WalletConnectViewModel.WC_SESSION_DB; import static com.alphawallet.ethereum.EthereumNetworkBase.MAINNET_ID; import android.app.Activity; -import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.ServiceConnection; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Handler; -import android.os.IBinder; import android.text.TextUtils; -import android.text.format.DateUtils; import android.view.View; import android.widget.Toast; @@ -37,7 +32,6 @@ import com.alphawallet.app.entity.Transaction; import com.alphawallet.app.entity.Version; import com.alphawallet.app.entity.Wallet; -import com.alphawallet.app.entity.WalletConnectActions; import com.alphawallet.app.entity.analytics.QrScanResultType; import com.alphawallet.app.entity.attestation.ImportAttestation; import com.alphawallet.app.interact.FetchWalletsInteract; @@ -49,7 +43,6 @@ import com.alphawallet.app.repository.LocaleRepositoryType; import com.alphawallet.app.repository.PreferenceRepositoryType; import com.alphawallet.app.repository.TokenRepository; -import com.alphawallet.app.repository.entity.RealmWCSession; import com.alphawallet.app.router.ExternalBrowserRouter; import com.alphawallet.app.router.ImportTokenRouter; import com.alphawallet.app.router.MyAddressRouter; @@ -60,20 +53,15 @@ import com.alphawallet.app.service.RealmManager; import com.alphawallet.app.service.TokensService; import com.alphawallet.app.service.TransactionsService; -import com.alphawallet.app.service.WalletConnectService; import com.alphawallet.app.ui.AddTokenActivity; import com.alphawallet.app.ui.HomeActivity; import com.alphawallet.app.ui.ImportWalletActivity; import com.alphawallet.app.ui.SendActivity; -import com.alphawallet.app.ui.WalletConnectActivity; import com.alphawallet.app.ui.WalletConnectV2Activity; import com.alphawallet.app.util.QRParser; import com.alphawallet.app.util.RateApp; import com.alphawallet.app.util.Utils; import com.alphawallet.app.util.ens.AWEnsResolver; -import com.alphawallet.app.walletconnect.WCClient; -import com.alphawallet.app.walletconnect.entity.WCUtils; -import com.alphawallet.app.walletconnect.util.WalletConnectHelper; import com.alphawallet.app.widget.EmailPromptView; import com.alphawallet.app.widget.QRCodeActionsView; import com.alphawallet.app.widget.WhatsNewView; @@ -103,9 +91,6 @@ import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; -import io.realm.Realm; -import io.realm.RealmResults; -import io.realm.Sort; import okhttp3.OkHttpClient; import okhttp3.Request; import timber.log.Timber; @@ -222,15 +207,12 @@ public LiveData defaultWallet() public GasService getGasService() { return gasService; } - public void prepare(Activity activity) + public void prepare() { progress.postValue(false); disposable = genericWalletInteract .find() - .subscribe(w -> { - onDefaultWallet(w); - initWalletConnectSessions(activity, w); - }, this::onError); + .subscribe(this::onDefaultWallet, this::onError); } public void onClean() @@ -462,21 +444,10 @@ public boolean requiresProcessing(String qrCode) private void startWalletConnect(Activity activity, String qrCode) { - Intent intent; - if (WalletConnectHelper.isWalletConnectV1(qrCode)) - { - intent = new Intent(activity, WalletConnectActivity.class); - intent.putExtra("qrCode", qrCode); - //intent.putExtra(C.EXTRA_CHAIN_ID, 0); - } - else - { - intent = new Intent(activity, WalletConnectV2Activity.class); - intent.putExtra("url", qrCode); - } + Intent intent = new Intent(activity, WalletConnectV2Activity.class); + intent.putExtra("url", qrCode); + activity.startActivity(intent); - //setResult(WALLET_CONNECT); - //finish(); } private void showActionSheet(Activity activity, QRResult qrResult) @@ -785,57 +756,6 @@ public void setCurrencyAndLocale(Context context) currencyRepository.setDefaultCurrency(preferenceRepository.getDefaultCurrency()); } - // Restart walletconnect sessions if required - private void initWalletConnectSessions(Activity activity, Wallet wallet) - { - List clientMap = new ArrayList<>(); - long cutOffTime = System.currentTimeMillis() - DateUtils.DAY_IN_MILLIS * 2; - try (Realm realm = realmManager.getRealmInstance(WC_SESSION_DB)) - { - RealmResults items = realm.where(RealmWCSession.class) - .greaterThan("lastUsageTime", cutOffTime) - .sort("lastUsageTime", Sort.DESCENDING) - .findAll(); - - for (RealmWCSession r : items) - { - String peerId = r.getPeerId(); - if (!TextUtils.isEmpty(peerId)) - { - // restart the session if it's not already known by the service - clientMap.add(WCUtils.createWalletConnectSession(activity, wallet, - r.getSession(), peerId, r.getRemotePeerId())); - } - } - } - - if (clientMap.size() > 0) - { - connectServiceAddClients(activity, clientMap); - } - } - - private void connectServiceAddClients(Activity activity, List clientMap) - { - ServiceConnection connection = new ServiceConnection() - { - @Override - public void onServiceConnected(ComponentName name, IBinder service) - { - WalletConnectService walletConnectService = ((WalletConnectService.LocalBinder) service).getService(); - walletConnectService.addClients(clientMap); - } - - @Override - public void onServiceDisconnected(ComponentName name) - { - Timber.d("Service disconnected"); - } - }; - - WCUtils.startServiceLocal(activity, connection, WalletConnectActions.CONNECT); - } - public boolean checkNewWallet(String address) { return preferenceRepository.isNewWallet(address); diff --git a/app/src/main/java/com/alphawallet/app/viewmodel/TokenIconViewModel.java b/app/src/main/java/com/alphawallet/app/viewmodel/TokenIconViewModel.java new file mode 100644 index 0000000000..b4379063e5 --- /dev/null +++ b/app/src/main/java/com/alphawallet/app/viewmodel/TokenIconViewModel.java @@ -0,0 +1,171 @@ +package com.alphawallet.app.viewmodel; + +import static com.alphawallet.app.repository.TokensRealmSource.IMAGES_DB; +import static com.alphawallet.app.util.Utils.ALPHAWALLET_REPO_NAME; +import static com.alphawallet.app.util.Utils.isValidUrl; + +import android.text.TextUtils; + +import androidx.annotation.NonNull; + +import com.alphawallet.app.entity.tokens.Token; +import com.alphawallet.app.repository.entity.RealmAuxData; +import com.alphawallet.app.service.AssetDefinitionService; +import com.alphawallet.app.service.RealmManager; +import com.alphawallet.app.service.TokensService; +import com.alphawallet.app.util.Utils; + +import org.json.JSONObject; +import org.web3j.crypto.Keys; + +import java.util.Iterator; + +import javax.inject.Inject; + +import dagger.hilt.android.lifecycle.HiltViewModel; +import io.reactivex.Single; +import io.realm.Realm; +import timber.log.Timber; + +@HiltViewModel +public class TokenIconViewModel extends BaseViewModel +{ + private final RealmManager realmManager; + private final AssetDefinitionService assetDefinitionService; + private final TokensService tokensService; + + @Inject + public TokenIconViewModel( + RealmManager realmManager, + AssetDefinitionService assetDefinitionService, + TokensService tokensService) + { + this.realmManager = realmManager; + this.assetDefinitionService = assetDefinitionService; + this.tokensService = tokensService; + } + + public String getTokenName(Token token) + { + return token.getName(assetDefinitionService, token.getTokenCount()); + } + + public Single getIconFallback(final Token token, boolean useContractURI) + { + if (useContractURI) + { + //attempt to pull contract URI + return token.getContractURI() + .map(uri -> handleURIResult(token, uri)); + } + else //use TW as last resort + { + return Single.fromCallable(() -> getFallbackUrlForToken(token)); + } + } + + private String handleURIResult(Token token, String uriResult) + { + String imageUrl = imageFromMetadata(uriResult); + + if (TextUtils.isEmpty(imageUrl)) + { + imageUrl = token.getFirstImageUrl(); //check for first NFT image + + if (TextUtils.isEmpty(imageUrl)) + { + imageUrl = getFallbackUrlForToken(token); + } + } + + return Utils.parseIPFS(imageUrl); + } + + @NonNull + private String getFallbackUrlForToken(@NonNull Token token) + { + String correctedAddr = Keys.toChecksumAddress(token.getAddress()); + return Utils.getTWTokenImageUrl(token.tokenInfo.chainId, correctedAddr); + } + + //Only store if it's not the AW iconassets url + public void storeImageUrl(long chainId, String address, String imageUrl) + { + if (!TextUtils.isEmpty(imageUrl) && isValidUrl(imageUrl) && !imageUrl.startsWith(ALPHAWALLET_REPO_NAME) && TextUtils.isEmpty(getTokenImageUrl(chainId, address))) + { + tokensService.addTokenImageUrl(chainId, address, imageUrl); + } + } + + private String getTokenImageUrl(long networkId, String address) + { + String url = ""; + String instanceKey = address.toLowerCase() + "-" + networkId; + try (Realm realm = realmManager.getRealmInstance(IMAGES_DB)) + { + RealmAuxData instance = realm.where(RealmAuxData.class) + .equalTo("instanceKey", instanceKey) + .findFirst(); + + if (instance != null) + { + url = instance.getResult(); + } + } + catch (Exception ex) + { + Timber.e(ex); + } + + return url; + } + + public String getTokenIcon(Token token) + { + //return getPrimaryIconURL(token); + //see if there's a stored icon + String icon = getTokenImageUrl(token.tokenInfo.chainId, token.tokenInfo.address); + if (TextUtils.isEmpty(icon)) + { + //attempt usual fetch + icon = getPrimaryIconURL(token); + } + else + { + System.out.println("FROM DB: " + icon); + } + + return icon; + } + + private String getPrimaryIconURL(Token token) + { + String correctedAddr = Keys.toChecksumAddress(token.getAddress()); + return Utils.getTokenImageUrl(correctedAddr); + } + + private String imageFromMetadata(String metaData) + { + try + { + JSONObject jsonData = new JSONObject(metaData); + Iterator keys = jsonData.keys(); + while (keys.hasNext()) + { + String key = keys.next(); + String value = jsonData.getString(key); + + if (key.startsWith("image")) + { + return value; + } + } + } + catch (Exception e) + { + // + } + + return ""; + } +} diff --git a/app/src/main/java/com/alphawallet/app/viewmodel/WalletConnectViewModel.java b/app/src/main/java/com/alphawallet/app/viewmodel/WalletConnectViewModel.java index ea9450a730..3d8a1f7d07 100644 --- a/app/src/main/java/com/alphawallet/app/viewmodel/WalletConnectViewModel.java +++ b/app/src/main/java/com/alphawallet/app/viewmodel/WalletConnectViewModel.java @@ -40,13 +40,11 @@ import com.alphawallet.app.service.RealmManager; import com.alphawallet.app.service.TokensService; import com.alphawallet.app.service.TransactionSendHandlerInterface; -import com.alphawallet.app.service.WalletConnectService; import com.alphawallet.app.walletconnect.AWWalletConnectClient; import com.alphawallet.app.walletconnect.WCClient; import com.alphawallet.app.walletconnect.WCSession; import com.alphawallet.app.walletconnect.entity.GetClientCallback; import com.alphawallet.app.walletconnect.entity.WCPeerMeta; -import com.alphawallet.app.walletconnect.entity.WCUtils; import com.alphawallet.app.web3.entity.WalletAddEthereumChainObject; import com.alphawallet.app.web3.entity.Web3Transaction; import com.alphawallet.hardware.SignatureFromKey; @@ -134,35 +132,6 @@ public class WalletConnectViewModel extends BaseViewModel implements Transaction .subscribe(w -> this.wallet = w, this::onError); } - public void startService(Context context) - { - ServiceConnection connection = new ServiceConnection() - { - @Override - public void onServiceConnected(ComponentName name, IBinder service) - { - WalletConnectService walletConnectService = ((WalletConnectService.LocalBinder) service).getService(); - Timber.tag(TAG).d("Service connected"); - for (String sessionId : clientBuffer.keySet()) - { - Timber.tag(TAG).d("put from buffer: %s", sessionId); - WCClient c = clientBuffer.get(sessionId); - walletConnectService.putClient(sessionId, c); - } - clientBuffer.clear(); - serviceReady.postValue(true); - } - - @Override - public void onServiceDisconnected(ComponentName name) - { - Timber.tag(TAG).d("Service disconnected"); - } - }; - - WCUtils.startServiceLocal(context, connection, WalletConnectActions.CONNECT); - } - public void prepare() { prepareDisposable = genericWalletInteract @@ -504,112 +473,6 @@ public MutableLiveData> sessions() return sessions; } - public void removePendingRequest(Activity activity, long id) - { - ServiceConnection connection = new ServiceConnection() - { - @Override - public void onServiceConnected(ComponentName name, IBinder service) - { - WalletConnectService walletConnectService = ((WalletConnectService.LocalBinder) service).getService(); - walletConnectService.removePendingRequest(id); - } - - @Override - public void onServiceDisconnected(ComponentName name) - { - //walletConnectService = null; - Timber.tag(TAG).d("Service disconnected"); - } - }; - - WCUtils.startServiceLocal(activity, connection, WalletConnectActions.CONNECT); - } - - public void getClient(Activity activity, String sessionId, GetClientCallback clientCb) - { - ServiceConnection connection = new ServiceConnection() - { - @Override - public void onServiceConnected(ComponentName name, IBinder service) - { - WalletConnectService walletConnectService = ((WalletConnectService.LocalBinder) service).getService(); - clientCb.getClient(walletConnectService.getClient(sessionId)); - } - - @Override - public void onServiceDisconnected(ComponentName name) - { - Timber.tag(TAG).d("Service disconnected"); - } - }; - - WCUtils.startServiceLocal(activity, connection, WalletConnectActions.CONNECT); - } - - public void putClient(Activity activity, String sessionId, WCClient client) - { - ServiceConnection connection = new ServiceConnection() - { - @Override - public void onServiceConnected(ComponentName name, IBinder service) - { - WalletConnectService walletConnectService = ((WalletConnectService.LocalBinder) service).getService(); - walletConnectService.putClient(sessionId, client); - awWalletConnectClient.updateNotification(); - } - - @Override - public void onServiceDisconnected(ComponentName name) - { - Timber.tag(TAG).d("Service disconnected"); - } - }; - - WCUtils.startServiceLocal(activity, connection, WalletConnectActions.CONNECT); - } - - public void disconnectSession(Activity activity, String sessionId) - { - ServiceConnection connection = new ServiceConnection() - { - @Override - public void onServiceConnected(ComponentName name, IBinder service) - { - WalletConnectService walletConnectService = ((WalletConnectService.LocalBinder) service).getService(); - walletConnectService.terminateClient(sessionId); - } - - @Override - public void onServiceDisconnected(ComponentName name) - { - Timber.tag(TAG).d("Service disconnected"); - } - }; - - WCUtils.startServiceLocal(activity, connection, WalletConnectActions.CONNECT); - } - - public void rejectRequest(Context ctx, String sessionId, long id, String message) - { - Intent bIntent = new Intent(ctx, WalletConnectService.class); - bIntent.setAction(String.valueOf(WalletConnectActions.REJECT.ordinal())); - bIntent.putExtra("sessionId", sessionId); - bIntent.putExtra("id", id); - bIntent.putExtra("message", message); - ctx.startService(bIntent); - } - - public void approveRequest(Context ctx, String sessionId, long id, String message) - { - Intent bIntent = new Intent(ctx, WalletConnectService.class); - bIntent.setAction(String.valueOf(WalletConnectActions.APPROVE.ordinal())); - bIntent.putExtra("sessionId", sessionId); - bIntent.putExtra("id", id); - bIntent.putExtra("message", message); - ctx.startService(bIntent); - } - public String getNetworkSymbol(long chainId) { NetworkInfo info = findDefaultNetworkInteract.getNetworkInfo(chainId); @@ -620,51 +483,6 @@ public String getNetworkSymbol(long chainId) return info.symbol; } - public void prepareIfRequired() - { - if (prepareDisposable == null) - { - prepare(); - } - } - - public void approveSwitchEthChain(Context context, long requestId, String sessionId, long chainId, boolean approve, boolean chainAvailable) - { - Intent i = new Intent(context, WalletConnectService.class); - i.setAction(String.valueOf(WalletConnectActions.SWITCH_CHAIN.ordinal())); - i.putExtra(C.EXTRA_WC_REQUEST_ID, requestId); - i.putExtra(C.EXTRA_SESSION_ID, sessionId); - i.putExtra(C.EXTRA_CHAIN_ID, chainId); - i.putExtra(C.EXTRA_APPROVED, approve); - i.putExtra(C.EXTRA_CHAIN_AVAILABLE, chainAvailable); - context.startService(i); - } - - public void approveAddEthereumChain(Context context, - long requestId, - String sessionId, - WalletAddEthereumChainObject chainObject, - boolean approved) - { - Intent i = new Intent(context, WalletConnectService.class); - i.setAction(String.valueOf(WalletConnectActions.ADD_CHAIN.ordinal())); - i.putExtra(C.EXTRA_WC_REQUEST_ID, requestId); - i.putExtra(C.EXTRA_SESSION_ID, sessionId); - i.putExtra(C.EXTRA_CHAIN_OBJ, chainObject); - i.putExtra(C.EXTRA_APPROVED, approved); - - if (approved) - { - // add only if not present - if (!isChainAdded(chainObject.getChainId())) - { - ethereumNetworkRepository.saveCustomRPCNetwork(chainObject.chainName, extractRpc(chainObject), chainObject.getChainId(), - chainObject.nativeCurrency.symbol, "", "", false, -1L); - } - } - context.startService(i); - } - private String extractRpc(WalletAddEthereumChainObject chainObject) { for (String thisRpc : chainObject.rpcUrls) @@ -711,13 +529,6 @@ public void endSession(String sessionId) } } - public void removeSessionsWithoutSignRecords(Context context) - { - getInactiveSessionIds(context, sessions -> { - deleteSessionsFromRealm(filterSessionsWithoutSignRecords(sessions), this::updateSessions); - }); - } - @NonNull private ArrayList filterSessionsWithoutSignRecords(List sessions) { @@ -737,49 +548,6 @@ public void updateSessions() sessions.postValue(walletConnectInteract.getSessions()); } - public void removeInactiveSessions(Context context) - { - getInactiveSessionIds(context, list -> { - deleteSessionsFromRealm(list, this::updateSessions); - }); - } - - // connects to service to check session state and gives inactive sessions - private void getInactiveSessionIds(Context context, GenericCallback> callback) - { - List sessionItems = walletConnectInteract.getSessions(); - ArrayList inactiveSessions = new ArrayList<>(); - ServiceConnection connection = new ServiceConnection() - { - @Override - public void onServiceConnected(ComponentName name, IBinder service) - { - WalletConnectService walletConnectService = ((WalletConnectService.LocalBinder) service).getService(); - // loop & populate sessions which are inactive - for (WalletConnectSessionItem item : sessionItems) - { - WCClient wcClient = walletConnectService.getClient(item.sessionId); - // if client is not connected ie: session inactive - if (wcClient == null || !wcClient.isConnected()) - { - inactiveSessions.add(item.sessionId); - } - } - callback.call(inactiveSessions); // return inactive sessions to caller - } - - @Override - public void onServiceDisconnected(ComponentName name) - { - //walletConnectService = null; - Timber.tag(TAG).d("Service disconnected"); - } - }; - Intent i = new Intent(context, WalletConnectService.class); // not specifying action as no need. we just need to bind to service - context.startService(i); - context.bindService(i, connection, Service.BIND_ABOVE_CLIENT); - } - // deletes the RealmWCSession objects with the given sessionIds present in the list private void deleteSessionsFromRealm(List sessionIds, Runnable onSuccess) { diff --git a/app/src/main/java/com/alphawallet/app/walletconnect/AWWalletConnectClient.java b/app/src/main/java/com/alphawallet/app/walletconnect/AWWalletConnectClient.java index 1f871d40f9..e30689d8d3 100644 --- a/app/src/main/java/com/alphawallet/app/walletconnect/AWWalletConnectClient.java +++ b/app/src/main/java/com/alphawallet/app/walletconnect/AWWalletConnectClient.java @@ -33,6 +33,7 @@ import com.alphawallet.app.repository.PreferenceRepositoryType; import com.alphawallet.app.service.GasService; import com.alphawallet.app.service.WalletConnectV2Service; +import com.alphawallet.app.ui.WalletConnectSessionActivity; import com.alphawallet.app.ui.WalletConnectV2Activity; import com.alphawallet.app.ui.widget.entity.ActionSheetCallback; import com.alphawallet.app.walletconnect.util.WCMethodChecker; @@ -43,7 +44,6 @@ import com.alphawallet.token.entity.EthereumMessage; import com.alphawallet.token.entity.SignMessageType; import com.alphawallet.token.entity.Signable; -import org.web3j.utils.Numeric; import com.walletconnect.android.Core; import com.walletconnect.android.CoreClient; import com.walletconnect.android.cacao.signature.SignatureType; @@ -53,6 +53,8 @@ import com.walletconnect.web3.wallet.client.Wallet.Model.Session; import com.walletconnect.web3.wallet.client.Web3Wallet; +import org.web3j.utils.Numeric; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -92,10 +94,10 @@ public AWWalletConnectClient(Context context, WalletConnectInteract walletConnec hasConnection = false; } - public void onSessionDelete(@NonNull Model.SessionDelete deletedSession) + /*public void onSessionDelete(@NonNull Model.SessionDelete deletedSession) { updateNotification(); - } + }*/ public void onSessionProposal(@NonNull Model.SessionProposal sessionProposal) { @@ -111,6 +113,11 @@ public void onSessionProposal(@NonNull Model.SessionProposal sessionProposal) context.startActivity(intent); } + public boolean hasWalletConnectSessions() + { + return !walletConnectInteract.getSessions().isEmpty(); + } + private boolean validChainId(List chains) { for (String chainId : chains) @@ -216,7 +223,7 @@ public void approve(Model.SessionProposal sessionProposal, List selected Params.SessionApprove approve = new Params.SessionApprove(proposerPublicKey, buildNamespaces(sessionProposal, selectedAccounts), sessionProposal.getRelayProtocol()); Web3Wallet.INSTANCE.approveSession(approve, sessionApprove -> { new Handler(Looper.getMainLooper()).postDelayed(() -> { - updateNotification(); + //updateNotification(); callback.onSessionProposalApproved(); }, 500); return null; @@ -288,14 +295,6 @@ public MutableLiveData> sessionItemMutableLiveDat return sessionItemMutableLiveData; } - public void updateNotification() - { - walletConnectInteract.fetchSessions(context, items -> { - sessionItemMutableLiveData.postValue(items); - updateService(context, items); - }); - } - private void updateService(Context context, List walletConnectSessionItems) { try @@ -338,7 +337,6 @@ public void disconnect(String sessionId, WalletConnectV2Callback callback) { Web3Wallet.INSTANCE.disconnectSession(new Params.SessionDisconnect(sessionId), sd -> null, this::onDisconnectError); callback.onSessionDisconnected(); - updateNotification(); } private Unit onDisconnectError(Model.Error error) @@ -666,6 +664,28 @@ public void onSessionRequest(@NonNull Model.SessionRequest sessionRequest, @NonN } } + @Override + public void onSessionDelete(@NonNull Model.SessionDelete sessionDelete) + { + + } + + public Intent getSessionIntent(Context appContext) + { + Intent intent; + List sessions = walletConnectInteract.getSessions(); + if (sessions.size() == 1) + { + intent = WalletConnectSessionActivity.newIntent(appContext, sessions.get(0)); + } + else + { + intent = new Intent(appContext, WalletConnectSessionActivity.class); + } + + return intent; + } + public interface WalletConnectV2Callback { default void onSessionProposalApproved() diff --git a/app/src/main/java/com/alphawallet/app/walletconnect/WCClient.kt b/app/src/main/java/com/alphawallet/app/walletconnect/WCClient.kt index 791f38ca34..a4f688c30d 100644 --- a/app/src/main/java/com/alphawallet/app/walletconnect/WCClient.kt +++ b/app/src/main/java/com/alphawallet/app/walletconnect/WCClient.kt @@ -1,22 +1,37 @@ package com.alphawallet.app.walletconnect import com.alphawallet.app.C -import com.alphawallet.app.walletconnect.entity.* +import com.alphawallet.app.walletconnect.entity.InvalidJsonRpcParamsException +import com.alphawallet.app.walletconnect.entity.JsonRpcError +import com.alphawallet.app.walletconnect.entity.JsonRpcErrorResponse +import com.alphawallet.app.walletconnect.entity.JsonRpcRequest +import com.alphawallet.app.walletconnect.entity.MessageType +import com.alphawallet.app.walletconnect.entity.WCEncryptionPayload +import com.alphawallet.app.walletconnect.entity.WCEthereumSignMessage +import com.alphawallet.app.walletconnect.entity.WCEthereumTransaction +import com.alphawallet.app.walletconnect.entity.WCMethod +import com.alphawallet.app.walletconnect.entity.WCPeerMeta +import com.alphawallet.app.walletconnect.entity.WCSessionUpdate +import com.alphawallet.app.walletconnect.entity.WCSocketMessage +import com.alphawallet.app.walletconnect.entity.ethTransactionSerializer import com.alphawallet.app.walletconnect.util.WCCipher import com.alphawallet.app.walletconnect.util.toByteArray import com.alphawallet.app.web3.entity.WalletAddEthereumChainObject -import com.alphawallet.ethereum.EthereumNetworkBase.MAINNET_ID import com.github.salomonbrys.kotson.fromJson import com.github.salomonbrys.kotson.registerTypeAdapter import com.github.salomonbrys.kotson.typeToken import com.google.gson.GsonBuilder import com.google.gson.JsonArray import com.google.gson.JsonSyntaxException -import okhttp3.* +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import okhttp3.WebSocket +import okhttp3.WebSocketListener import okio.ByteString -import org.web3j.utils.Numeric import timber.log.Timber -import java.util.* +import java.util.Date +import java.util.UUID import java.util.concurrent.TimeUnit open class WCClient : WebSocketListener() { @@ -52,8 +67,6 @@ open class WCClient : WebSocketListener() { else return null } - private var handshakeId: Long = -1 - var accounts: List? = null private set @@ -67,10 +80,6 @@ open class WCClient : WebSocketListener() { .retryOnConnectionFailure(true) .build(); - fun chainIdVal(): Long { - return chainId?.toLong() ?: 0 - } - var onFailure: (Throwable) -> Unit = { _ -> Unit } var onDisconnect: (code: Int, reason: String) -> Unit = { _, _ -> Unit } var onSessionRequest: (id: Long, peer: WCPeerMeta) -> Unit = { _, _ -> Unit } @@ -94,9 +103,9 @@ open class WCClient : WebSocketListener() { listeners.forEach { it.onOpen(webSocket, response) } val session = - this.session ?: throw IllegalStateException("session can't be null on connection open") + this.session!! val peerId = - this.peerId ?: throw IllegalStateException("peerId can't be null on connection open") + this.peerId!! // The Session.topic channel is used to listen session request messages only. subscribe(session.topic) // The peerId channel is used to listen to all messages sent to this httpClient. @@ -172,39 +181,6 @@ open class WCClient : WebSocketListener() { socket = httpClient.newWebSocket(request, this) } - fun setupSession(accounts: List, _chainId: Long) { - this.chainId = _chainId.toString(); - this.accounts = accounts; - } - - fun approveSession(accounts: List, _chainId: Long): Boolean { - if (handshakeId <= 0) { - onFailure(Throwable("handshakeId must be greater than 0 on session approve")) - } - var useChainId: Long = _chainId - if (this.chainId?.toIntOrNull() != 1) useChainId = _chainId - chainId = useChainId.toString() - this.accounts = accounts; - - val result = WCApproveSessionResponse( - chainId = useChainId, - accounts = accounts, - peerId = peerId, - peerMeta = peerMeta - ) - val response = JsonRpcResponse( - id = handshakeId, - result = result - ) - - return encryptAndSend(gson.toJson(response)) - } - - fun sendPing(): Boolean { - Timber.d("==> ping") - return socket?.send("ping") ?: false - } - fun updateSession( accounts: List? = null, chainId: Long? = null, @@ -224,114 +200,15 @@ open class WCClient : WebSocketListener() { return encryptAndSend(gson.toJson(request)) } - fun rejectSession(message: String = "Session rejected"): Boolean { - check(handshakeId > 0) { "handshakeId must be greater than 0" } - val response = JsonRpcErrorResponse( - id = handshakeId, - error = JsonRpcError.serverError( - message = message - ) - ) - return encryptAndSend(gson.toJson(response)) - } - fun killSession(): Boolean { updateSession(approved = false) return disconnect() } - fun approveRequest(id: Long, result: T): Boolean { - val response = JsonRpcResponse( - id = id, - result = result - ) - return encryptAndSend(gson.toJson(response)) - } - - fun rejectRequest(id: Long, message: String = "Rejected by the user"): Boolean { - val response = JsonRpcErrorResponse( - id = id, - error = JsonRpcError.serverError( - message = message - ) - ) - return encryptAndSend(gson.toJson(response)) - } - - fun switchChain( - requestId: Long, - chainId: Long, - success: Boolean, - chainAvailable: Boolean = true - ): Boolean { - Timber.tag(TAG).d( - "switchChain: id: %s, chainId: %s, success: %s, chainAvailable: %s", - requestId, - chainId, - success, - chainAvailable - ); - var response:String; - if (!chainAvailable) { - val errorResponse = JsonRpcErrorResponse( - id = requestId, - error = JsonRpcError.unrecognisedChain( - message = "Unrecognized chain ID" - ) - ) - return encryptAndSend(gson.toJson(errorResponse)) - } - if (success) { - this.chainId = chainId.toString(); - updateSession() // will use updated chainId - response = gson.toJson(JsonRpcResponse( - id = requestId, // json rpc request id - result = null - )) - } else { - response = gson.toJson(JsonRpcErrorResponse( - id = requestId, - error = JsonRpcError.serverError( - message = "Rejected by user" - )) - ) - } - return encryptAndSend(response) - } - - fun addChain( - requestId: Long, - chainObj: WalletAddEthereumChainObject, - success: Boolean - ): Boolean { - Timber.tag(TAG).d( - "addChain: id: %s, chainId: %s, success: %s", - requestId, - chainObj.getChainId(), - success - ); - return if (success) { - this.chainId = chainObj.getChainId().toString() - updateSession() // updated session with new chain Id - encryptAndSend( - gson.toJson( - JsonRpcResponse(id = requestId, result = null) - ) - ) - } else { - val errorResponse = JsonRpcErrorResponse( - id = requestId, - error = JsonRpcError.serverError("Rejected by user") - ) - encryptAndSend(gson.toJson(errorResponse)) - } - } - private fun decryptMessage(text: String): String { val message = gson.fromJson(text) val encrypted = gson.fromJson(message.payload) - val session = this.session - ?: throw IllegalStateException("Session is null") + val session = this.session!! return String(WCCipher.decrypt(encrypted, session.key.toByteArray()), Charsets.UTF_8) } @@ -365,91 +242,6 @@ open class WCClient : WebSocketListener() { private fun handleRequest(request: JsonRpcRequest) { Timber.tag(TAG).d("handleRequest: %s", request.toString()) - when (request.method) { - WCMethod.SESSION_REQUEST -> { - val param = gson.fromJson>(request.params) - .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id) - handshakeId = request.id - remotePeerId = param.peerId - chainId = when (param.chainId.isNullOrEmpty()) { - true -> MAINNET_ID.toString() - false -> param.chainId - } - onSessionRequest(request.id, param.peerMeta) - } - WCMethod.SESSION_UPDATE -> { - val param = gson.fromJson>(request.params) - .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id) - if (!param.approved) { - killSession() - } - } - WCMethod.ETH_SIGN -> { - signRequest(request, WCEthereumSignMessage.WCSignType.MESSAGE) - } - WCMethod.ETH_PERSONAL_SIGN -> { - signRequest(request, WCEthereumSignMessage.WCSignType.PERSONAL_MESSAGE) - } - WCMethod.ETH_SIGN_TYPE_DATA, - WCMethod.ETH_SIGN_TYPE_DATA_V3, - WCMethod.ETH_SIGN_TYPE_DATA_V4 -> { - signRequest(request, WCEthereumSignMessage.WCSignType.TYPED_MESSAGE) - } - WCMethod.ETH_SIGN_TRANSACTION -> { - val param = gson.fromJson>(request.params) - .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id) - onEthSignTransaction(request.id, param) - } - WCMethod.ETH_SEND_TRANSACTION -> { - val param = gson.fromJson>(request.params) - .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id) - onEthSendTransaction(request.id, param) - } - WCMethod.GET_ACCOUNTS -> { - onGetAccounts(request.id) - } - WCMethod.SWITCH_ETHEREUM_CHAIN -> { - handleSwitchChain(request) - } - WCMethod.ADD_ETHEREUM_CHAIN -> { - handleAddChain(request) - } - else -> {} - } - } - - private fun handleAddChain(request: JsonRpcRequest) - { - Timber.d("WCMethod: addEthereumChain") - val param: WCAddEthChain = gson.fromJson>(request.params) - .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id) - Timber.tag(TAG).d("addChainRequest: $param") - onAddEthereumChain(request.id, param.toWalletAddEthereumObject()) - } - - private fun handleSwitchChain(request: JsonRpcRequest) - { - Timber.d("WCMethod: switchEthereumChain") - val param = gson.fromJson>(request.params) - .firstOrNull() ?: throw InvalidJsonRpcParamsException(request.id) - val newChainId: Long = Numeric.toBigInt(param.chainId).toLong(); - if (newChainId != chainIdVal()) { - onSwitchEthereumChain(request.id, newChainId) - } else { - // auto accept if same chain - switchChain(request.id, chainIdVal(), true); - } - } - - private fun signRequest(request: JsonRpcRequest, signType: WCEthereumSignMessage.WCSignType) - { - val params = gson.fromJson>(request.params) - if (params.size < 2) - throw InvalidJsonRpcParamsException(request.id) - onEthSign( - request.id, - WCEthereumSignMessage(params, signType) - ) } private fun subscribe(topic: String): Boolean { @@ -466,8 +258,7 @@ open class WCClient : WebSocketListener() { private fun encryptAndSend(result: String): Boolean { Timber.d("==> message $result") - val session = this.session - ?: throw IllegalStateException("Session is null") + val session = this.session!! val payload = gson.toJson( WCCipher.encrypt( result.toByteArray(Charsets.UTF_8), @@ -491,21 +282,4 @@ open class WCClient : WebSocketListener() { fun disconnect(): Boolean { return socket?.close(1000, null) ?: false } - - fun addSocketListener(listener: WebSocketListener) { - listeners.add(listener) - } - - fun removeSocketListener(listener: WebSocketListener) { - listeners.remove(listener) - } - - fun resetState() { - handshakeId = -1 - isConnected = false - session = null - peerId = null - remotePeerId = null - peerMeta = null - } } diff --git a/app/src/main/java/com/alphawallet/app/walletconnect/entity/WCUtils.java b/app/src/main/java/com/alphawallet/app/walletconnect/entity/WCUtils.java deleted file mode 100644 index 72f8b0ea36..0000000000 --- a/app/src/main/java/com/alphawallet/app/walletconnect/entity/WCUtils.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.alphawallet.app.walletconnect.entity; - -import android.app.Activity; -import android.app.ActivityManager; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; - -import com.alphawallet.app.C; -import com.alphawallet.app.R; -import com.alphawallet.app.entity.Wallet; -import com.alphawallet.app.entity.WalletConnectActions; -import com.alphawallet.app.repository.entity.RealmWCSession; -import com.alphawallet.app.service.WalletConnectService; -import com.alphawallet.app.walletconnect.WCClient; -import com.alphawallet.app.walletconnect.WCSession; - -import java.util.ArrayList; -import java.util.Collections; - -public abstract class WCUtils -{ - public static void startServiceLocal(Context context, ServiceConnection connection, WalletConnectActions action) - { - Intent i = new Intent(context, WalletConnectService.class); - i.setAction(String.valueOf(action.ordinal())); - ActivityManager.RunningAppProcessInfo myProcess = new ActivityManager.RunningAppProcessInfo(); - ActivityManager.getMyMemoryState(myProcess); - boolean isInBackground = myProcess.importance != ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; - if (!isInBackground) - { - context.startService(i); - if (connection != null) context.bindService(i, connection, Context.BIND_ABOVE_CLIENT); - } - } - - public static WCClient createWalletConnectSession(Activity activity, Wallet wallet, WCSession session, String peerId, String remotePeerId) - { - WCClient client = new WCClient(); - - WCPeerMeta peerMeta = new WCPeerMeta( - activity.getString(R.string.app_name), - C.ALPHAWALLET_WEB, - wallet.address, - new ArrayList<>(Collections.singleton(C.ALPHAWALLET_LOGO_URI)) - ); - - client.connect(session, peerMeta, peerId, remotePeerId); - - return client; - } -} diff --git a/app/src/main/java/com/alphawallet/app/widget/ActionSheetDialog.java b/app/src/main/java/com/alphawallet/app/widget/ActionSheetDialog.java index 0572b884f2..8a9e546fa6 100644 --- a/app/src/main/java/com/alphawallet/app/widget/ActionSheetDialog.java +++ b/app/src/main/java/com/alphawallet/app/widget/ActionSheetDialog.java @@ -37,7 +37,6 @@ import com.alphawallet.app.service.TokensService; import com.alphawallet.app.ui.HomeActivity; import com.alphawallet.app.ui.TransactionSuccessActivity; -import com.alphawallet.app.ui.WalletConnectActivity; import com.alphawallet.app.ui.widget.entity.ActionSheetCallback; import com.alphawallet.app.ui.widget.entity.GasWidgetInterface; import com.alphawallet.app.util.Utils; @@ -128,10 +127,6 @@ public ActionSheetDialog(@NonNull Activity activity, Web3Transaction tx, Token t { mode = ActionSheetMode.SEND_TRANSACTION_DAPP; } - else if (activity instanceof WalletConnectActivity) - { - mode = ActionSheetMode.SEND_TRANSACTION_WC; - } else { mode = ActionSheetMode.SEND_TRANSACTION; @@ -151,7 +146,7 @@ else if (activity instanceof WalletConnectActivity) transaction = new Transaction(tx, token.tokenInfo.chainId, ts.getCurrentAddress()); transaction.transactionInput = Transaction.decoder.decodeInput(candidateTransaction, token.tokenInfo.chainId, token.getWallet()); - balanceDisplay.setupBalance(token, tokensService, transaction); + balanceDisplay.setupBalance(token, transaction); networkDisplay.setNetwork(token.tokenInfo.chainId); gasWidgetInterface = setupGasWidget(); @@ -845,13 +840,6 @@ public void gotSignature(SignatureFromKey signature) actionSheetCallback.getAuthorisation(signCallback); } - //Takes gas estimate from calling activity (eg WalletConnectActivity) and updates dialog -// public void setGasEstimate(BigInteger estimate) -// { -// gasWidgetInterface.setGasEstimate(estimate); -// functionBar.setPrimaryButtonEnabled(true); -// } - @Override public void setGasEstimate(GasEstimate estimate) { diff --git a/app/src/main/java/com/alphawallet/app/widget/BalanceDisplayWidget.java b/app/src/main/java/com/alphawallet/app/widget/BalanceDisplayWidget.java index a0720842db..bef9f9d083 100644 --- a/app/src/main/java/com/alphawallet/app/widget/BalanceDisplayWidget.java +++ b/app/src/main/java/com/alphawallet/app/widget/BalanceDisplayWidget.java @@ -37,12 +37,12 @@ public BalanceDisplayWidget(Context context, @Nullable AttributeSet attrs) tokenIcon = findViewById(R.id.token_icon); } - public void setupBalance(Token token, TokensService tokenService, Transaction tx) + public void setupBalance(Token token, Transaction tx) { if (token.isNonFungible()) { tokenIcon.setVisibility(View.VISIBLE); - tokenIcon.bindData(token, tokenService); + tokenIcon.bindData(token); } else { @@ -72,4 +72,4 @@ else if (transaction == null || transaction.transactionInput == null || transact String newBalanceVal = BalanceUtils.getScaledValueScientific(new BigDecimal(balanceAfterTransaction), token.tokenInfo.decimals); newBalance.setText(getContext().getString(R.string.new_balance, newBalanceVal, token.getSymbol())); } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/alphawallet/app/widget/EventDetailWidget.java b/app/src/main/java/com/alphawallet/app/widget/EventDetailWidget.java index ef50d2d7f3..72579e6ee1 100644 --- a/app/src/main/java/com/alphawallet/app/widget/EventDetailWidget.java +++ b/app/src/main/java/com/alphawallet/app/widget/EventDetailWidget.java @@ -47,7 +47,7 @@ public EventDetailWidget(Context context, AttributeSet attrs) public void setupView(RealmAuxData data, Token token, AssetDefinitionService svs, String eventAmount) { holdingView.setVisibility(View.VISIBLE); - icon.bindData(token, svs); + icon.bindData(token); title.setText(data.getTitle(getContext())); symbol.setText(token.getSymbol()); int resourceId; @@ -76,7 +76,7 @@ public void setupView(RealmAuxData data, Token token, AssetDefinitionService svs public void setupTransactionView(Transaction tx, Token token, AssetDefinitionService svs, String supplimentalInfo) { holdingView.setVisibility(View.VISIBLE); - icon.bindData(token, svs); + icon.bindData(token); symbol.setText(token.getSymbol()); title.setVisibility(View.GONE); if (supplimentalInfo.charAt(0) == '-') diff --git a/app/src/main/java/com/alphawallet/app/widget/InputAmount.java b/app/src/main/java/com/alphawallet/app/widget/InputAmount.java index 4d49ba8deb..cd04f42178 100644 --- a/app/src/main/java/com/alphawallet/app/widget/InputAmount.java +++ b/app/src/main/java/com/alphawallet/app/widget/InputAmount.java @@ -62,7 +62,6 @@ public class InputAmount extends LinearLayout private Realm realm; private Realm tickerRealm; private TokensService tokensService; - private AssetDefinitionService assetService; private BigInteger gasPriceEstimate = BigInteger.ZERO; private BigDecimal exactAmount = BigDecimal.ZERO; private final Handler handler = new Handler(Looper.getMainLooper()); @@ -105,14 +104,13 @@ public InputAmount(Context context, AttributeSet attrs) * @param assetDefinitionService * @param svs */ - public void setupToken(@NotNull Token token, @Nullable AssetDefinitionService assetDefinitionService, + public void setupToken(@NotNull Token token, @NotNull TokensService svs, @NotNull AmountReadyCallback amountCallback) { this.token = token; this.tokensService = svs; - this.assetService = assetDefinitionService; this.amountReadyCallback = amountCallback; - icon.bindData(token, assetService); + icon.bindData(token); header.getChainName().setChainID(token.tokenInfo.chainId); updateAvailableBalance(); @@ -315,7 +313,7 @@ private void startTickerListener() private void showCrypto() { - icon.bindData(token, assetService); + icon.bindData(token); symbolText.setText(token.getSymbol()); availableSymbol.setText(token.getSymbol()); availableAmount.setText(token.getStringBalanceForUI(5)); diff --git a/app/src/main/java/com/alphawallet/app/widget/TokenIcon.java b/app/src/main/java/com/alphawallet/app/widget/TokenIcon.java index 248c62fdc3..c2752b29cd 100644 --- a/app/src/main/java/com/alphawallet/app/widget/TokenIcon.java +++ b/app/src/main/java/com/alphawallet/app/widget/TokenIcon.java @@ -1,9 +1,13 @@ package com.alphawallet.app.widget; import static androidx.core.content.ContextCompat.getColorStateList; +import static com.alphawallet.app.util.Utils.ALPHAWALLET_REPO_NAME; +import static com.alphawallet.app.util.Utils.TRUST_ICON_REPO_BASE; import static com.alphawallet.ethereum.EthereumNetworkBase.MAINNET_ID; +import android.app.Activity; import android.content.Context; +import android.content.ContextWrapper; import android.content.res.TypedArray; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; @@ -12,7 +16,6 @@ import android.os.Looper; import android.text.TextUtils; import android.util.AttributeSet; -import android.util.Pair; import android.view.View; import android.widget.ImageView; import android.widget.ProgressBar; @@ -20,6 +23,8 @@ import androidx.annotation.Nullable; import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.lifecycle.ViewModelProvider; +import androidx.lifecycle.ViewModelStoreOwner; import com.alphawallet.app.R; import com.alphawallet.app.entity.tokendata.TokenGroup; @@ -27,13 +32,11 @@ import com.alphawallet.app.repository.CurrencyRepository; import com.alphawallet.app.repository.EthereumNetworkBase; import com.alphawallet.app.repository.EthereumNetworkRepository; -import com.alphawallet.app.service.AssetDefinitionService; import com.alphawallet.app.service.TickerService; -import com.alphawallet.app.service.TokensService; import com.alphawallet.app.ui.widget.TokensAdapterCallback; -import com.alphawallet.app.ui.widget.entity.IconItem; import com.alphawallet.app.ui.widget.entity.StatusType; import com.alphawallet.app.util.Utils; +import com.alphawallet.app.viewmodel.TokenIconViewModel; import com.bumptech.glide.Glide; import com.bumptech.glide.load.DataSource; import com.bumptech.glide.load.engine.GlideException; @@ -44,7 +47,10 @@ import com.bumptech.glide.request.target.Target; import org.jetbrains.annotations.NotNull; -import org.web3j.crypto.Keys; + +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; public class TokenIcon extends ConstraintLayout { @@ -61,12 +67,21 @@ public class TokenIcon extends ConstraintLayout private final boolean squareToken; private TokensAdapterCallback tokensAdapterCallback; private volatile Token token; - - private final RequestListener requestListenerTW = new RequestListener() + private final TokenIconViewModel viewModel; + @Nullable + private Disposable disposable; + private String tokenName; + private StatusType currentStatus; + private Request currentRq; + /** + * Prevent glide dumping log errors - it is expected that load will fail + */ + private final RequestListener requestListener = new RequestListener<>() { @Override public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { + loadFromAltRepo(model); return false; } @@ -76,11 +91,7 @@ public boolean onResourceReady(Drawable resource, Object model, Target if (model == null) return false; if (token != null) { - IconItem.secondaryFound(token.tokenInfo.chainId, token.getAddress()); - } - if (token == null || !model.toString().toLowerCase().contains(token.getAddress())) - { - return false; + viewModel.storeImageUrl(token.tokenInfo.chainId, token.tokenInfo.address, model.toString()); } handler.post(() -> { @@ -92,37 +103,6 @@ public boolean onResourceReady(Drawable resource, Object model, Target return false; } }; - private String tokenName; - private StatusType currentStatus; - private String fallbackIconUrl; - private Request currentRq; - /** - * Prevent glide dumping log errors - it is expected that load will fail - */ - private final RequestListener requestListener = new RequestListener<>() - { - @Override - public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) - { - if (model != null && token != null && model.toString().toLowerCase().contains(token.getAddress())) - { - handler.post(() -> loadFromAltRepo()); - } - - return false; - } - - @Override - public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) - { - handler.post(() -> { - textIcon.setVisibility(View.GONE); - icon.setVisibility(View.VISIBLE); - icon.setImageDrawable(resource); - }); - return false; - } - }; public TokenIcon(Context context, AttributeSet attrs) { @@ -143,6 +123,7 @@ public TokenIcon(Context context, AttributeSet attrs) currentStatus = StatusType.NONE; chainIcon = findViewById(R.id.status_chain_icon); chainIconBackground = findViewById(R.id.chain_icon_background); + viewModel = new ViewModelProvider((ViewModelStoreOwner) getActivity()).get(TokenIconViewModel.class); bindViews(); } @@ -183,20 +164,26 @@ public void clearLoad() { currentRq.pause(); currentRq.clear(); + currentRq = null; handler.removeCallbacksAndMessages(null); token = null; } + + if (disposable != null && !disposable.isDisposed()) + { + disposable.dispose(); + } } /** * This method is necessary to call from the binder to show information correctly. * * @param token Token object - * @param assetDefinition Asset Definition Service for Icons */ - public void bindData(Token token, @NotNull AssetDefinitionService assetDefinition) + public void bindData(Token token) { this.token = token; + clearLoad(); if (token.isEthereum()) { @@ -210,48 +197,22 @@ public void bindData(Token token, @NotNull AssetDefinitionService assetDefinitio } else { - this.tokenName = token.getName(assetDefinition, token.getTokenCount()); - Pair iconFallback = assetDefinition.getFallbackUrlForToken(token); - String mainIcon = iconFallback.second ? iconFallback.first : getPrimaryIconURL(token); - this.fallbackIconUrl = iconFallback.second ? getPrimaryIconURL(token) : iconFallback.first; - bind(token, new IconItem(mainIcon, token.tokenInfo.chainId, token.getAddress())); + this.tokenName = viewModel.getTokenName(token); + String mainIcon = viewModel.getTokenIcon(token); + bind(token, mainIcon); } } - public void bindData(Token token) - { - if (token == null) return; - this.tokenName = token.getName(); - this.fallbackIconUrl = Utils.getTWTokenImageUrl(token.tokenInfo.chainId, token.getAddress()); - bind(token, getIconUrl(token)); - } - public void bindData(long chainId) { + clearLoad(); loadImageFromResource(EthereumNetworkRepository.getChainLogo(chainId)); } - public void bindData(Token token, @NotNull TokensService svs) - { - this.handler.removeCallbacks(null); - if (token == null) return; - this.tokenName = token.getName(); - this.fallbackIconUrl = svs.getFallbackUrlForToken(token); - - if (token.group == TokenGroup.SPAM) - { - bindSpam(token); - } - else - { - bind(token, getIconUrl(token)); - } - } - - private void bind(Token token, IconItem iconItem) + private void bind(Token token, String iconUrl) { bindCommon(token); - displayTokenIcon(iconItem); + displayTokenIcon(iconUrl); } private void bindSpam(Token token) @@ -300,42 +261,23 @@ private void setupDefaultIcon() /** * Try to fetch Token Icon from the Token URL. */ - private void displayTokenIcon(IconItem iconItem) + private void displayTokenIcon(String iconUrl) { setupDefaultIcon(); - if (token.isEthereum() - || token.getWallet().equalsIgnoreCase(token.getAddress()) - || iconItem.useTextSymbol()) return; - - if (iconItem.usePrimary()) + if (token.tokenInfo.address.equalsIgnoreCase("0xfaafdc07907ff5120a76b34b731b278c38d6043c")) { - final RequestOptions optionalCircleCrop = squareToken || iconItem.getUrl().startsWith(Utils.ALPHAWALLET_REPO_NAME) ? new RequestOptions() : new RequestOptions().circleCrop(); + System.out.println("YOLESS"); + } - currentRq = Glide.with(this) - .load(iconItem.getUrl()) + final RequestOptions optionalCircleCrop = squareToken || iconUrl.startsWith(ALPHAWALLET_REPO_NAME) ? new RequestOptions() : new RequestOptions().circleCrop(); + + currentRq = Glide.with(this) + .load(iconUrl) .placeholder(R.drawable.ic_token_eth) .apply(optionalCircleCrop) .listener(requestListener) .into(new DrawableImageViewTarget(icon)).getRequest(); - } - else - { - loadFromAltRepo(); - } - } - - private String getPrimaryIconURL(Token token) - { - String correctedAddr = Keys.toChecksumAddress(token.getAddress()); - return Utils.getTokenImageUrl(correctedAddr); - } - - private IconItem getIconUrl(Token token) - { - String correctedAddr = Keys.toChecksumAddress(token.getAddress()); - String tURL = Utils.getTokenImageUrl(correctedAddr); - return new IconItem(tURL, token.tokenInfo.chainId, token.getAddress()); } public void setStatusIcon(StatusType type) @@ -375,19 +317,53 @@ public void setStatusIcon(StatusType type) /** * Attempt to load the icon from the Database icon or TW icon repo */ - private void loadFromAltRepo() + private void loadFromAltRepo(Object model) { - if (!Utils.stillAvailable(getContext()) || this.fallbackIconUrl == null) return; + //check how to load from alt repo or ignore + String checkUrl = model != null ? model.toString() : null; + if (!Utils.stillAvailable(getContext()) || token == null || TextUtils.isEmpty(checkUrl)) + { + return; + } + + //check heuristic: + //1. If it's the AW iconassets repo then check using contract URI + //2. If it's not Trust repo URL then check using Trust repo + //3. If it is the Trust repo then stop + + boolean useContractURI; + + if (checkUrl.startsWith(ALPHAWALLET_REPO_NAME)) + { + useContractURI = true; + } + else if (!checkUrl.startsWith(TRUST_ICON_REPO_BASE)) + { + useContractURI = false; + } + else + { + return; //we checked Trust repo, this is the final check + } + + disposable = viewModel.getIconFallback(token, useContractURI) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(this::loadImageAlt); + } + private void loadImageAlt(String fileUri) + { final RequestOptions optionalCircleCrop = squareToken ? new RequestOptions() : new RequestOptions().circleCrop(); currentRq = Glide.with(this) - .load(this.fallbackIconUrl) - .apply(optionalCircleCrop) - .listener(requestListenerTW) - .into(new DrawableImageViewTarget(iconSecondary)).getRequest(); + .load(fileUri) + .apply(optionalCircleCrop) + .listener(requestListener) + .into(new DrawableImageViewTarget(iconSecondary)).getRequest(); } + /** * This method is used to set TextIcon and make Icon hidden as there is no icon available for the token. * @@ -500,4 +476,18 @@ public void setAttestationIcon(String image, String symbol, long chain) setIsAttestation(symbol, chain); } } + + private Activity getActivity() + { + Context context = getContext(); + while (context instanceof ContextWrapper) + { + if (context instanceof Activity) + { + return (Activity) context; + } + context = ((ContextWrapper) context).getBaseContext(); + } + return null; + } } diff --git a/app/src/main/java/com/alphawallet/app/widget/TokenInfoHeaderView.java b/app/src/main/java/com/alphawallet/app/widget/TokenInfoHeaderView.java index 27bd504211..d6b0d7a300 100644 --- a/app/src/main/java/com/alphawallet/app/widget/TokenInfoHeaderView.java +++ b/app/src/main/java/com/alphawallet/app/widget/TokenInfoHeaderView.java @@ -37,7 +37,7 @@ public TokenInfoHeaderView(Context context) public TokenInfoHeaderView(Context context, Token token, TokensService svs) { this(context); - icon.bindData(token, svs); + icon.bindData(token); if (!token.isEthereum()) icon.setChainIcon(token.tokenInfo.chainId); setAmount(token.getFixedFormattedBalance()); setSymbol(token.tokenInfo.symbol); diff --git a/app/src/main/res/drawable/ic_wallet_connect.xml b/app/src/main/res/drawable/ic_wallet_connect.xml index 8d0ff691cc..b477ea3a18 100644 --- a/app/src/main/res/drawable/ic_wallet_connect.xml +++ b/app/src/main/res/drawable/ic_wallet_connect.xml @@ -1,18 +1,13 @@ - - - - + + + + + + + + + + + + diff --git a/app/src/main/res/layout/layout_manage_token_search.xml b/app/src/main/res/layout/layout_manage_token_search.xml index 5158de135f..bd63b0cbbe 100644 --- a/app/src/main/res/layout/layout_manage_token_search.xml +++ b/app/src/main/res/layout/layout_manage_token_search.xml @@ -1,52 +1,69 @@ - - + + + + android:id="@+id/layout_search_tokens" + android:layout_width="0dp" + android:layout_height="@dimen/massive_44" + android:layout_marginStart="@dimen/tiny_8" + android:layout_marginTop="@dimen/tiny_8" + android:layout_marginEnd="@dimen/tiny_8" + android:layout_marginBottom="@dimen/tiny_8" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toStartOf="@+id/icon_wc_active" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + android:background="@drawable/background_round_search" + android:gravity="center_vertical" + android:orientation="horizontal"> + android:layout_width="@dimen/base_24" + android:layout_height="@dimen/base_24" + android:layout_marginStart="@dimen/small_12" + android:src="@drawable/ic_search_small" + app:tint="?android:textColorSecondary" /> + android:id="@+id/edit_search" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" + android:layout_marginStart="@dimen/tiny_8" + android:layout_marginEnd="@dimen/standard_16" + android:background="@null" + android:clickable="false" + android:focusable="false" + android:fontFamily="@font/font_regular" + android:hint="@string/search_for_tokens" + android:imeOptions="actionSearch" + android:maxLength="20" + android:maxLines="1" /> + android:id="@+id/click_layer" + android:layout_width="0dp" + android:layout_height="@dimen/optimal_30" + app:layout_constraintStart_toStartOf="@+id/layout_search_tokens" + app:layout_constraintEnd_toEndOf="@+id/layout_search_tokens" + app:layout_constraintTop_toTopOf="@+id/layout_search_tokens" + app:layout_constraintBottom_toBottomOf="@+id/layout_search_tokens" + android:background="@color/transparent" /> - \ No newline at end of file + diff --git a/build.sh b/build.sh index 33f57f37c6..b6888a2bef 100644 --- a/build.sh +++ b/build.sh @@ -4,4 +4,4 @@ #cd dmz && ../gradlew -i build && ../gradlew -i test && cd .. cd lib && ../gradlew -i build && ../gradlew -i test && cd .. #cd util && ../gradlew -i build && ../gradlew -i test && cd .. -./gradlew clean jacocoTestNoAnalyticsDebugUnitTestReport -x lint -x detekt +#./gradlew clean jacocoTestNoAnalyticsDebugUnitTestReport -x lint -x detekt diff --git a/gradle.properties b/gradle.properties index a337e45051..dfbb156238 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,6 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 android.useAndroidX=true android.enableJetifier=true android.jetifier.ignorelist=bcprov-jdk15on,bcprov-jdk18on,com.squareup.moshi -org.gradle.configuration-cache=true # Enables namespacing of each library's R class so that its R class includes only the # resources declared in the library itself and none from the library's dependencies, diff --git a/lib/build.gradle b/lib/build.gradle index e31be6432c..df6a5c0751 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -1,29 +1,5 @@ plugins { - id 'com.android.library' -} - -android { - namespace 'com.alphawallet' - compileSdk 34 - - defaultConfig { - minSdk 24 - targetSdk 34 - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles "consumer-rules.pro" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 - } + id 'java-library' } dependencies { diff --git a/lib/src/main/java/com/alphawallet/attestation/Attestation.java b/lib/src/main/java/com/alphawallet/attestation/Attestation.java index d481da4103..242348dfd4 100644 --- a/lib/src/main/java/com/alphawallet/attestation/Attestation.java +++ b/lib/src/main/java/com/alphawallet/attestation/Attestation.java @@ -46,6 +46,14 @@ public Attestation() { public int getVersion() { return version.getValue().intValueExact(); + /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + { + return version.getValue().intValueExact(); + } + else + { + return version.getValue().intValue(); + }*/ } public void setVersion(int version) { @@ -54,6 +62,14 @@ public void setVersion(int version) { public int getSerialNumber() { return serialNumber.getValue().intValueExact(); + /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + { + return serialNumber.getValue().intValueExact(); + } + else + { + return serialNumber.getValue().intValue(); + }*/ } public void setSerialNumber(long serialNumber) { @@ -140,6 +156,14 @@ public List getSmartcontracts() { Iterator it = smartcontracts.iterator(); while(it.hasNext()) { res.add(((ASN1Integer) it.next()).getValue().longValueExact()); + /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + { + res.add(((ASN1Integer) it.next()).getValue().longValueExact()); + } + else + { + res.add(((ASN1Integer) it.next()).getValue().longValue()); + }*/ } return res; } @@ -182,6 +206,19 @@ public boolean isValidX509() { if (version.getValue().intValueExact() != 0 && version.getValue().intValueExact() != 1 && version.getValue().intValueExact() != 2) { return false; } + /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + { + if (version.getValue().intValueExact() != 0 && version.getValue().intValueExact() != 1 && version.getValue().intValueExact() != 2) { + return false; + } + } + else + { + if (version.getValue().intValue() != 0 && version.getValue().intValue() != 1 && version.getValue().intValue() != 2) { + return false; + } + }*/ + if (issuer == null || issuer.getRDNs().length == 0) { return false; }