diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 799f59ecb3..c16555c436 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -377,6 +377,10 @@
android:name=".ui.NotificationSettingsActivity"
android:label="@string/title_notifications" />
+
+
fromTokens;
+ public List fromTokens;
@SerializedName("toTokens")
@Expose
- public List toTokens;
+ public List toTokens;
}
diff --git a/app/src/main/java/com/alphawallet/app/entity/lifi/FeeCost.java b/app/src/main/java/com/alphawallet/app/entity/lifi/FeeCost.java
index bbbd5cd645..5a68b58ca0 100644
--- a/app/src/main/java/com/alphawallet/app/entity/lifi/FeeCost.java
+++ b/app/src/main/java/com/alphawallet/app/entity/lifi/FeeCost.java
@@ -3,8 +3,6 @@
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
-import java.util.ArrayList;
-
public class FeeCost
{
@SerializedName("name")
@@ -17,7 +15,7 @@ public class FeeCost
@SerializedName("token")
@Expose
- public Token token;
+ public LifiToken token;
@SerializedName("amount")
@Expose
diff --git a/app/src/main/java/com/alphawallet/app/entity/lifi/GasCost.java b/app/src/main/java/com/alphawallet/app/entity/lifi/GasCost.java
index d2ccd5b39b..f504311c7f 100644
--- a/app/src/main/java/com/alphawallet/app/entity/lifi/GasCost.java
+++ b/app/src/main/java/com/alphawallet/app/entity/lifi/GasCost.java
@@ -15,5 +15,5 @@ public class GasCost
@SerializedName("token")
@Expose
- public Token token;
+ public LifiToken token;
}
\ No newline at end of file
diff --git a/app/src/main/java/com/alphawallet/app/entity/lifi/Token.java b/app/src/main/java/com/alphawallet/app/entity/lifi/LifiToken.java
similarity index 97%
rename from app/src/main/java/com/alphawallet/app/entity/lifi/Token.java
rename to app/src/main/java/com/alphawallet/app/entity/lifi/LifiToken.java
index 83ec1225a6..04d86cd78a 100644
--- a/app/src/main/java/com/alphawallet/app/entity/lifi/Token.java
+++ b/app/src/main/java/com/alphawallet/app/entity/lifi/LifiToken.java
@@ -5,7 +5,7 @@
import java.util.Objects;
-public class Token
+public class LifiToken
{
@SerializedName("address")
@Expose
@@ -47,7 +47,7 @@ public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- Token lToken = (Token) o;
+ LifiToken lToken = (LifiToken) o;
return address.equals(lToken.address) && symbol.equals(lToken.symbol);
}
diff --git a/app/src/main/java/com/alphawallet/app/repository/PreferenceRepositoryType.java b/app/src/main/java/com/alphawallet/app/repository/PreferenceRepositoryType.java
index 84224dacca..fe134be878 100644
--- a/app/src/main/java/com/alphawallet/app/repository/PreferenceRepositoryType.java
+++ b/app/src/main/java/com/alphawallet/app/repository/PreferenceRepositoryType.java
@@ -1,6 +1,9 @@
package com.alphawallet.app.repository;
+import android.util.Pair;
+
import com.alphawallet.app.entity.CurrencyItem;
+import com.alphawallet.app.entity.tokens.Token;
import java.util.Set;
@@ -138,4 +141,8 @@ public interface PreferenceRepositoryType
boolean isPostNotificationsPermissionRequested(String address);
void setPostNotificationsPermissionRequested(String address, boolean hasRequested);
+
+ void setLastSentToken(Token address);
+
+ Pair getLastSentToken();
}
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 640a4966ec..e342a8d365 100644
--- a/app/src/main/java/com/alphawallet/app/repository/SharedPreferenceRepository.java
+++ b/app/src/main/java/com/alphawallet/app/repository/SharedPreferenceRepository.java
@@ -3,12 +3,14 @@
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
+import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager;
import com.alphawallet.app.C;
import com.alphawallet.app.entity.CurrencyItem;
+import com.alphawallet.app.entity.tokens.Token;
import java.util.HashSet;
import java.util.Locale;
@@ -52,6 +54,8 @@ public class SharedPreferenceRepository implements PreferenceRepositoryType {
private static final String LAUNCH_COUNT = "launch_count";
private static final String NEW_WALLET = "new_wallet_";
private static final String WATCH_ONLY = "watch_only";
+ private static final String LAST_SENT_TOKEN_ADDRESS = "last_sent_token_address";
+ private static final String LAST_SENT_TOKEN_CHAIN_ID = "last_sent_token_chain_id";
private final SharedPreferences pref;
@@ -460,6 +464,22 @@ public void setPostNotificationsPermissionRequested(String address, boolean hasR
pref.edit().putBoolean(getAddressKey(POST_NOTIFICATIONS_PERMISSION_REQUESTED, address), hasRequested).apply();
}
+ @Override
+ public void setLastSentToken(Token token)
+ {
+ pref.edit().putString(LAST_SENT_TOKEN_ADDRESS, token.tokenInfo.address).apply();
+ pref.edit().putLong(LAST_SENT_TOKEN_CHAIN_ID, token.tokenInfo.chainId).apply();
+ }
+
+ @Override
+ public Pair getLastSentToken()
+ {
+ return new Pair<>(
+ pref.getString(LAST_SENT_TOKEN_ADDRESS, ""),
+ pref.getLong(LAST_SENT_TOKEN_CHAIN_ID, 0)
+ );
+ }
+
@NonNull
private String getAddressKey(String key, String address)
{
diff --git a/app/src/main/java/com/alphawallet/app/router/SendTokenRouter.java b/app/src/main/java/com/alphawallet/app/router/SendTokenRouter.java
index 43def2fde1..31138dc662 100644
--- a/app/src/main/java/com/alphawallet/app/router/SendTokenRouter.java
+++ b/app/src/main/java/com/alphawallet/app/router/SendTokenRouter.java
@@ -1,25 +1,39 @@
package com.alphawallet.app.router;
-
import android.app.Activity;
import android.content.Intent;
import com.alphawallet.app.C;
-import com.alphawallet.app.entity.QRResult;
-import com.alphawallet.app.entity.Wallet;
import com.alphawallet.app.entity.tokens.Token;
import com.alphawallet.app.ui.SendActivity;
-public class SendTokenRouter {
- public void open(Activity context, String address, String symbol, int decimals, Wallet wallet, Token token, long chainId) {
+public class SendTokenRouter
+{
+
+ // From token detail page
+ public void open(Activity context, Token token)
+ {
+ Intent intent = new Intent(context, SendActivity.class);
+ intent.putExtra(C.EXTRA_CONTRACT_ADDRESS, token.getAddress());
+ intent.putExtra(C.EXTRA_NETWORKID, token.tokenInfo.chainId);
+ intent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ context.startActivityForResult(intent, C.COMPLETED_TRANSACTION);
+ }
+
+ // From address scan
+ public void open(Activity context, String recipientAddress)
+ {
+ //TODO Implement
+ Intent intent = new Intent(context, SendActivity.class);
+ intent.putExtra(C.EXTRA_ADDRESS, recipientAddress);
+ intent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ context.startActivityForResult(intent, C.COMPLETED_TRANSACTION);
+ }
+
+ // From bottom nav
+ public void open(Activity context)
+ {
Intent intent = new Intent(context, SendActivity.class);
- intent.putExtra(C.EXTRA_CONTRACT_ADDRESS, address);
- intent.putExtra(C.EXTRA_ADDRESS, token.getAddress());
- intent.putExtra(C.EXTRA_NETWORKID, chainId);
- intent.putExtra(C.EXTRA_SYMBOL, symbol);
- intent.putExtra(C.EXTRA_DECIMALS, decimals);
- intent.putExtra(C.Key.WALLET, wallet);
- intent.putExtra(C.EXTRA_AMOUNT, (QRResult)null);
intent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
context.startActivityForResult(intent, C.COMPLETED_TRANSACTION);
}
diff --git a/app/src/main/java/com/alphawallet/app/router/TransferRequestRouter.java b/app/src/main/java/com/alphawallet/app/router/TransferRequestRouter.java
new file mode 100644
index 0000000000..8f6dca6ced
--- /dev/null
+++ b/app/src/main/java/com/alphawallet/app/router/TransferRequestRouter.java
@@ -0,0 +1,20 @@
+package com.alphawallet.app.router;
+
+
+import android.app.Activity;
+import android.content.Intent;
+
+import com.alphawallet.app.C;
+import com.alphawallet.app.entity.QRResult;
+import com.alphawallet.app.ui.TransferRequestActivity;
+
+public class TransferRequestRouter
+{
+ public void open(Activity context, QRResult result)
+ {
+ Intent intent = new Intent(context, TransferRequestActivity.class);
+ intent.putExtra(C.EXTRA_AMOUNT, result);
+ intent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ context.startActivityForResult(intent, C.COMPLETED_TRANSACTION);
+ }
+}
diff --git a/app/src/main/java/com/alphawallet/app/service/SwapService.java b/app/src/main/java/com/alphawallet/app/service/SwapService.java
index fab5804e2e..3f58f77f15 100644
--- a/app/src/main/java/com/alphawallet/app/service/SwapService.java
+++ b/app/src/main/java/com/alphawallet/app/service/SwapService.java
@@ -4,7 +4,7 @@
import com.alphawallet.app.C;
import com.alphawallet.app.entity.lifi.RouteOptions;
-import com.alphawallet.app.entity.lifi.Token;
+import com.alphawallet.app.entity.lifi.LifiToken;
import com.alphawallet.app.repository.SwapRepository;
import com.alphawallet.app.util.BalanceUtils;
import com.alphawallet.app.util.JsonUtils;
@@ -125,8 +125,8 @@ public Single getConnections(long from, long to)
return Single.fromCallable(() -> fetchPairs(from, to));
}
- public Single getQuote(Token source,
- Token dest,
+ public Single getQuote(LifiToken source,
+ LifiToken dest,
String address,
String amount,
String slippage,
@@ -135,8 +135,8 @@ public Single getQuote(Token source,
return Single.fromCallable(() -> fetchQuote(source, dest, address, amount, slippage, allowExchanges));
}
- public Single getRoutes(Token source,
- Token dest,
+ public Single getRoutes(LifiToken source,
+ LifiToken dest,
String address,
String amount,
String slippage,
@@ -180,8 +180,8 @@ public String fetchPairs(long fromChain, long toChain)
return executeRequest(builder.build().toString());
}
- public String fetchQuote(Token source,
- Token dest,
+ public String fetchQuote(LifiToken source,
+ LifiToken dest,
String address,
String amount,
String slippage,
@@ -200,8 +200,8 @@ public String fetchQuote(Token source,
return executeRequest(builder.build().toString());
}
- public String fetchRoutes(Token source,
- Token dest,
+ public String fetchRoutes(LifiToken source,
+ LifiToken dest,
String address,
String amount,
String slippage,
diff --git a/app/src/main/java/com/alphawallet/app/ui/AddTokenActivity.java b/app/src/main/java/com/alphawallet/app/ui/AddTokenActivity.java
index 93a136864d..77963ea4f0 100644
--- a/app/src/main/java/com/alphawallet/app/ui/AddTokenActivity.java
+++ b/app/src/main/java/com/alphawallet/app/ui/AddTokenActivity.java
@@ -249,14 +249,14 @@ private void finishAndLaunchSend()
}
else
{
- viewModel.showSend(this, currentResult, token);
+ viewModel.showSend(this, currentResult);
finish();
}
}
else
{
//launch send payment screen for eth transaction
- viewModel.showSend(this, currentResult, viewModel.getToken(currentResult.chainId, viewModel.wallet().getValue().address));
+ viewModel.showSend(this, currentResult);
finish();
}
}
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 ad88271363..19e34cf3e5 100644
--- a/app/src/main/java/com/alphawallet/app/ui/DappBrowserFragment.java
+++ b/app/src/main/java/com/alphawallet/app/ui/DappBrowserFragment.java
@@ -1560,7 +1560,7 @@ public void handleQRCode(int resultCode, Intent data, FragmentMessenger messenge
viewModel.track(Analytics.Action.SCAN_QR_CODE_SUCCESS, props);
//EIP681 payment request scanned, should go to send
- viewModel.showSend(getContext(), result);
+ viewModel.showSend(getActivity(), result);
break;
case FUNCTION_CALL:
diff --git a/app/src/main/java/com/alphawallet/app/ui/Erc20DetailActivity.java b/app/src/main/java/com/alphawallet/app/ui/Erc20DetailActivity.java
index c22994dd96..063d9c5407 100644
--- a/app/src/main/java/com/alphawallet/app/ui/Erc20DetailActivity.java
+++ b/app/src/main/java/com/alphawallet/app/ui/Erc20DetailActivity.java
@@ -425,7 +425,7 @@ public void handleTokenScriptFunction(String function, List selectio
@Override
public void showSend()
{
- viewModel.showSendToken(this, wallet, token);
+ viewModel.showSendToken(this, token);
}
@Override
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 a6c6e4d1d8..4cfd1ce745 100644
--- a/app/src/main/java/com/alphawallet/app/ui/MyAddressActivity.java
+++ b/app/src/main/java/com/alphawallet/app/ui/MyAddressActivity.java
@@ -198,13 +198,13 @@ private void showPointOfSaleMode()
{
setContentView(R.layout.activity_eip681);
initViews();
- findViewById(R.id.toolbar_title).setVisibility(View.GONE);
- setTitle("");
+ setTitle(getString(R.string.generate_payment_request));
displayAddress = Keys.toChecksumAddress(wallet.address);
networkInfo = viewModel.getEthereumNetworkRepository().getNetworkByChain(overrideNetwork);
currentMode = AddressMode.MODE_POS;
layoutInputAmount.setVisibility(View.VISIBLE);
-
+ copyWalletName = findViewById(R.id.copy_wallet_name);
+ copyWalletName.setVisibility(View.GONE);
amountInput = findViewById(R.id.input_amount);
setupPOSMode(networkInfo);
}
@@ -214,6 +214,7 @@ 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.setAmount("");
+ amountInput.focus();
updateCryptoAmount(BigDecimal.ZERO);
}
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 e3c7479eb6..ac8e2f613f 100644
--- a/app/src/main/java/com/alphawallet/app/ui/SendActivity.java
+++ b/app/src/main/java/com/alphawallet/app/ui/SendActivity.java
@@ -1,9 +1,7 @@
package com.alphawallet.app.ui;
-import static com.alphawallet.app.C.Key.WALLET;
import static com.alphawallet.app.widget.AWalletAlertDialog.ERROR;
import static com.alphawallet.app.widget.AWalletAlertDialog.WARNING;
-import static com.alphawallet.ethereum.EthereumNetworkBase.MAINNET_ID;
import android.app.Activity;
import android.content.Intent;
@@ -12,8 +10,6 @@
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.View;
-import android.widget.TextView;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
@@ -25,9 +21,8 @@
import com.alphawallet.app.analytics.Analytics;
import com.alphawallet.app.entity.AnalyticsProperties;
import com.alphawallet.app.entity.CryptoFunctions;
-import com.alphawallet.app.entity.EIP681Type;
+import com.alphawallet.app.entity.ErrorEnvelope;
import com.alphawallet.app.entity.GasEstimate;
-import com.alphawallet.app.entity.NetworkInfo;
import com.alphawallet.app.entity.Operation;
import com.alphawallet.app.entity.QRResult;
import com.alphawallet.app.entity.SignAuthenticationCallback;
@@ -38,6 +33,7 @@
import com.alphawallet.app.entity.tokens.Token;
import com.alphawallet.app.repository.EthereumNetworkBase;
import com.alphawallet.app.repository.EthereumNetworkRepository;
+import com.alphawallet.app.router.TransferRequestRouter;
import com.alphawallet.app.service.GasService;
import com.alphawallet.app.ui.QRScanning.QRScannerActivity;
import com.alphawallet.app.ui.widget.entity.ActionSheetCallback;
@@ -54,10 +50,10 @@
import com.alphawallet.app.widget.FunctionButtonBar;
import com.alphawallet.app.widget.InputAddress;
import com.alphawallet.app.widget.InputAmount;
+import com.alphawallet.app.widget.SelectTokenDialog;
import com.alphawallet.app.widget.SignTransactionDialog;
import com.alphawallet.hardware.SignatureFromKey;
import com.alphawallet.token.entity.SalesOrderMalformed;
-import com.alphawallet.token.tools.Convert;
import com.alphawallet.token.tools.Numeric;
import com.alphawallet.token.tools.ParseMagicLink;
@@ -75,28 +71,31 @@
import timber.log.Timber;
@AndroidEntryPoint
-public class SendActivity extends BaseActivity implements AmountReadyCallback, StandardFunctionInterface, AddressReadyCallback, ActionSheetCallback
+public class SendActivity extends BaseActivity implements
+ AmountReadyCallback,
+ StandardFunctionInterface,
+ AddressReadyCallback,
+ ActionSheetCallback,
+ SelectTokenDialog.OnTokenClickListener
{
private static final BigDecimal NEGATIVE = BigDecimal.ZERO.subtract(BigDecimal.ONE);
-
- SendViewModel viewModel;
-
+ private final Handler handler = new Handler();
+ private SendViewModel viewModel;
private Wallet wallet;
private Token token;
- private final Handler handler = new Handler();
private AWalletAlertDialog dialog;
-
- private QRResult currentResult;
-
private InputAmount amountInput;
private InputAddress addressInput;
- private String sendAddress;
+ private FunctionButtonBar functionBar;
+ private String sendAddress = null;
private String ensAddress;
- private BigDecimal sendAmount;
- private BigDecimal sendGasPrice;
+ private BigDecimal sendAmount = NEGATIVE;
+ private BigDecimal sendGasPrice = BigDecimal.ZERO;
private ActionSheetDialog confirmationDialog;
+ private final ActivityResultLauncher getGasSettings = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
+ result -> confirmationDialog.setCurrentGasIndex(result));
+ private SelectTokenDialog selectTokenDialog;
private AWalletAlertDialog alertDialog;
-
@Nullable
private Disposable calcGasCost;
@@ -104,68 +103,79 @@ public class SendActivity extends BaseActivity implements AmountReadyCallback, S
protected void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
+
setContentView(R.layout.activity_send);
+
toolbar();
- viewModel = new ViewModelProvider(this)
- .get(SendViewModel.class);
+ setTitle("Send");
- String contractAddress = getIntent().getStringExtra(C.EXTRA_CONTRACT_ADDRESS);
- long currentChain = getIntent().getLongExtra(C.EXTRA_NETWORKID, MAINNET_ID);
- wallet = getIntent().getParcelableExtra(WALLET);
- token = viewModel.getToken(currentChain, getIntent().getStringExtra(C.EXTRA_ADDRESS));
- QRResult result = getIntent().getParcelableExtra(C.EXTRA_AMOUNT);
+ initViewModel();
- viewModel.transactionFinalised().observe(this, this::txWritten);
- viewModel.transactionError().observe(this, this::txError);
+ initViews();
- sendAddress = null;
- sendGasPrice = BigDecimal.ZERO;
- sendAmount = NEGATIVE;
+// evaluateQrResult(getIntent().getParcelableExtra(C.EXTRA_AMOUNT));
- if (!checkTokenValidity(currentChain, contractAddress))
- {
- return;
- }
+ viewModel.prepare();
+ }
- setTitle(getString(R.string.action_send_tkn, token.getShortName()));
- setupTokenContent();
+ private void initViews()
+ {
+ amountInput = findViewById(R.id.input_amount);
+ addressInput = findViewById(R.id.input_address);
+ functionBar = findViewById(R.id.layoutButtons);
- if (result != null)
- {
- //restore payment request
- validateEIP681Request(result, true);
- }
+ addressInput.setAddressCallback(this);
+ amountInput.setListener(v -> showTokenSelectDialog());
}
- @Override
- protected void onResume()
+ private void initViewModel()
{
- super.onResume();
+ viewModel = new ViewModelProvider(this)
+ .get(SendViewModel.class);
+ viewModel.wallet().observe(this, this::onWallet);
+ viewModel.tokens().observe(this, this::onTokens);
+ viewModel.transactionFinalised().observe(this, this::txWritten);
+ viewModel.transactionError().observe(this, this::txError);
+ viewModel.error().observe(this, this::onError);
+ }
- QRResult result = getIntent().getParcelableExtra(C.EXTRA_AMOUNT);
+ private void onWallet(Wallet wallet)
+ {
+ this.wallet = wallet;
- if (result != null && (result.type == EIP681Type.PAYMENT || result.type == EIP681Type.TRANSFER))
+ String recipientAddress = getIntent().getStringExtra(C.EXTRA_ADDRESS);
+ String tokenAddress = getIntent().getStringExtra(C.EXTRA_CONTRACT_ADDRESS);
+ long tokenChainId = getIntent().getLongExtra(C.EXTRA_NETWORKID, -1);
+
+ if (!TextUtils.isEmpty(recipientAddress)) // From address qr
+ {
+ addressInput.setAddress(recipientAddress);
+ showTokenSelectDialog();
+ }
+ else if (!TextUtils.isEmpty(tokenAddress) && tokenChainId != -1) // From token detail
+ {
+ token = viewModel.getToken(tokenChainId, tokenAddress);
+ setupTokenContent(token);
+ }
+ else // From bottom nav
{
- handleClick("", R.string.action_next);
+ showTokenSelectDialog();
}
}
- private boolean checkTokenValidity(long currentChain, String contractAddress)
+ private void onTokens(List tokens)
{
- if (token == null || token.tokenInfo == null)
+ selectTokenDialog = new SelectTokenDialog(tokens, this, this);
+ if (!selectTokenDialog.isShowing())
{
- //bad token - try to load from service
- token = viewModel.getToken(currentChain, contractAddress);
-
- if (token == null)
- {
- //TODO: possibly invoke token finder in tokensService
- finish();
- }
+ selectTokenDialog.show();
}
+ }
- return (token != null);
+ private void onError(ErrorEnvelope errorEnvelope)
+ {
+ displayToast(errorEnvelope.message);
}
private void onBack()
@@ -284,7 +294,7 @@ else if (requestCode == C.BARCODE_READER_REQUEST_CODE)
break;
default:
Timber.tag("SEND").e(String.format(getString(R.string.barcode_error_format),
- "Code: " + resultCode
+ "Code: " + resultCode
));
break;
}
@@ -344,109 +354,26 @@ private void calculateEstimateDialog()
private void validateEIP681Request(QRResult result, boolean overrideNetwork)
{
- if (dialog != null) dialog.dismiss();
- //check chain
- if (result == null)
- {
- displayScanError();
- return;
- }
-
- NetworkInfo info = viewModel.getNetworkInfo(result.chainId);
- if (info == null)
- {
- displayScanError();
- return;
- }
- else if (result.type != EIP681Type.ADDRESS && result.chainId != token.tokenInfo.chainId && token.isEthereum())
- {
- //Display chain change warning
- currentResult = result;
- showChainChangeDialog(result.chainId);
- return;
- }
-
- TextView sendText = findViewById(R.id.text_payment_request);
-
switch (result.type)
{
case ADDRESS:
addressInput.setAddress(result.getAddress());
break;
-
case PAYMENT:
- //correct chain and asset type
- String ethAmount = Convert.getConvertedValue(new BigDecimal(result.weiValue), Convert.Unit.ETHER.getFactor());
- sendText.setVisibility(View.VISIBLE);
- 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.setAmount(ethAmount);
- setupTokenContent();
- break;
-
case TRANSFER:
- Token resultToken = viewModel.getToken(result.chainId, result.getAddress());
- if (resultToken == null)
- {
- currentResult = result;
- showTokenFetch();
- viewModel.fetchToken(result.chainId, result.getAddress(), wallet.address);
- }
- else if (resultToken.isERC20())
- {
- //ERC20 send request
- token = resultToken;
- setupTokenContent();
- amountInput.setupToken(token, viewModel.getAssetDefinitionService(), viewModel.getTokenService(), this);
- //convert token amount into scaled value
- String convertedAmount = Convert.getConvertedValue(result.tokenAmount, token.tokenInfo.decimals);
- amountInput.setAmount(convertedAmount);
- addressInput.setAddress(result.functionToAddress);
- sendText.setVisibility(View.VISIBLE);
- sendText.setText(getString(R.string.token_transfer_request, resultToken.getFullName()));
- }
- //TODO: Handle NFT eg ERC721
+ new TransferRequestRouter().open(this, result);
break;
-
case FUNCTION_CALL:
//Generic function call, not handled yet
displayScanError(R.string.toast_qr_code_no_address, getString(R.string.no_tokens));
if (result.functionToAddress != null)
addressInput.setAddress(result.functionToAddress);
break;
-
default:
displayScanError();
}
}
- private void showChainChangeDialog(long chainId)
- {
- if (dialog != null && dialog.isShowing()) dialog.dismiss();
- dialog = new AWalletAlertDialog(this);
- dialog.setIcon(AWalletAlertDialog.WARNING);
- dialog.setTitle(R.string.change_chain_request);
- dialog.setMessage(R.string.change_chain_message);
- dialog.setButtonText(R.string.dialog_ok);
- dialog.setButtonListener(v -> {
- //we should change the chain.
- token = viewModel.getToken(chainId, token.getAddress());
- amountInput.setupToken(token, viewModel.getAssetDefinitionService(), viewModel.getTokenService(), this);
- dialog.dismiss();
- validateEIP681Request(currentResult, false);
- });
- dialog.setSecondaryButtonText(R.string.action_cancel);
- dialog.setSecondaryButtonListener(v -> {
- dialog.dismiss();
- //proceed without changing the chain
- currentResult.chainId = token.tokenInfo.chainId;
- validateEIP681Request(currentResult, false);
- });
- dialog.show();
- }
-
private void displayScanError()
{
if (dialog != null && dialog.isShowing()) dialog.dismiss();
@@ -486,20 +413,19 @@ protected void onDestroy()
// addressInput.setEnsNodeNotSyncCallback(null); // prevent leak by removing reference to activity method
}
- private void setupTokenContent()
+ private void setupTokenContent(Token token)
{
- amountInput = findViewById(R.id.input_amount);
+ this.token = token;
+ setTitle(getString(R.string.action_send_tkn, token.getSymbol()));
amountInput.setupToken(token, viewModel.getAssetDefinitionService(), viewModel.getTokenService(), this);
- addressInput = findViewById(R.id.input_address);
- addressInput.setAddressCallback(this);
addressInput.setChainOverrideForWalletConnect(token.tokenInfo.chainId);
//addressInput.setEnsHandlerNodeSyncFlag(true); // allow node sync
//addressInput.setEnsNodeNotSyncCallback(this::showNodeNotSyncSheet); // callback to invoke if node not synced
- FunctionButtonBar functionBar = findViewById(R.id.layoutButtons);
functionBar.revealButtons();
List functions = new ArrayList<>(Collections.singletonList(R.string.action_next));
functionBar.setupFunctions(this, functions);
viewModel.startGasCycle(token.tokenInfo.chainId);
+ amountInput.focus();
}
@Override
@@ -507,7 +433,7 @@ public void amountReady(BigDecimal value, BigDecimal gasPrice)
{
//validate that we have sufficient balance
if ((token.isEthereum() && token.balance.subtract(value).compareTo(BigDecimal.ZERO) > 0) // if sending base ethereum then check we have more than just the value
- || (token.getBalanceRaw().subtract(value).compareTo(BigDecimal.ZERO) >= 0)) // contract token, check sufficient token balance (gas widget will check sufficient gas)
+ || (token.getBalanceRaw().subtract(value).compareTo(BigDecimal.ZERO) >= 0)) // contract token, check sufficient token balance (gas widget will check sufficient gas)
{
sendAmount = value;
sendGasPrice = gasPrice;
@@ -554,7 +480,7 @@ public void addressReady(String address, String ensName)
private void calculateTransactionCost()
{
if ((calcGasCost != null && !calcGasCost.isDisposed()) ||
- (confirmationDialog != null && confirmationDialog.isShowing())) return;
+ (confirmationDialog != null && confirmationDialog.isShowing())) return;
if (sendAmount.compareTo(NEGATIVE) > 0 && Utils.isAddressValid(sendAddress))
{
@@ -568,10 +494,10 @@ private void calculateTransactionCost()
calculateEstimateDialog();
//form payload and calculate tx cost
calcGasCost = viewModel.calculateGasEstimate(wallet, transactionBytes, token.tokenInfo.chainId, txDestAddress, BigDecimal.ZERO)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(estimate -> checkConfirm(estimate, transactionBytes, txDestAddress, txSendAddress),
- error -> handleError(error, transactionBytes, token.getAddress(), txSendAddress));
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(estimate -> checkConfirm(estimate, transactionBytes, txDestAddress, txSendAddress),
+ error -> handleError(error, transactionBytes, token.getAddress(), txSendAddress));
}
}
@@ -589,15 +515,15 @@ private void checkConfirm(final GasEstimate estimate, final byte[] transactionBy
BigInteger ethValue = token.isEthereum() ? sendAmount.toBigInteger() : BigInteger.ZERO;
long leafCode = amountInput.isSendAll() ? -2 : -1;
Web3Transaction w3tx = new Web3Transaction(
- new Address(txSendAddress),
- token.isEthereum() ? null : new Address(token.getAddress()),
- new Address(wallet.address),
- ethValue,
- sendGasPrice.toBigInteger(),
- estimate.getValue(),
- -1,
- Numeric.toHexString(transactionBytes),
- leafCode);
+ new Address(txSendAddress),
+ token.isEthereum() ? null : new Address(token.getAddress()),
+ new Address(wallet.address),
+ ethValue,
+ sendGasPrice.toBigInteger(),
+ estimate.getValue(),
+ -1,
+ Numeric.toHexString(transactionBytes),
+ leafCode);
if (estimate.hasError() || estimate.getValue().equals(BigInteger.ZERO))
{
@@ -607,7 +533,7 @@ private void checkConfirm(final GasEstimate estimate, final byte[] transactionBy
{
if (dialog != null && dialog.isShowing()) dialog.dismiss();
confirmationDialog = new ActionSheetDialog(this, w3tx, token, ensAddress,
- resolvedAddress, viewModel.getTokenService(), this);
+ resolvedAddress, viewModel.getTokenService(), this);
confirmationDialog.setCanceledOnTouchOutside(false);
confirmationDialog.show();
sendAmount = NEGATIVE;
@@ -652,9 +578,6 @@ public void dismissed(String txHash, long callbackId, boolean actionCompleted)
}
}
- ActivityResultLauncher getGasSettings = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
- result -> confirmationDialog.setCurrentGasIndex(result));
-
@Override
public ActivityResultLauncher gasSelectLauncher()
{
@@ -678,6 +601,11 @@ public WalletType getWalletType()
private void txWritten(TransactionReturn txData)
{
confirmationDialog.transactionWritten(txData.hash);
+ viewModel.setLastSentToken(token);
+// viewModel.setLastSentTokenAddress(txData.tx.contract != null ?
+// txData.tx.contract.toString() :
+// txData.tx.sender.toString()
+// );
}
//Transaction failed to be sent
@@ -703,7 +631,7 @@ private void estimateError(GasEstimate estimate, final Web3Transaction w3tx, fin
dialog.setTitle(estimate.hasError() ?
R.string.dialog_title_gas_estimation_failed :
R.string.confirm_transaction
- );
+ );
String message = estimate.hasError() ?
getString(R.string.dialog_message_gas_estimation_failed, estimate.getError()) :
getString(R.string.error_transaction_may_fail);
@@ -773,4 +701,19 @@ void showTxnTimeoutDialog()
});
dialog.show();
}
+
+ private void showTokenSelectDialog()
+ {
+ viewModel.fetchTokens();
+ }
+
+ @Override
+ public void onTokenClicked(Token token)
+ {
+ if (selectTokenDialog != null && selectTokenDialog.isShowing())
+ {
+ selectTokenDialog.dismiss();
+ }
+ setupTokenContent(token);
+ }
}
diff --git a/app/src/main/java/com/alphawallet/app/ui/SwapActivity.java b/app/src/main/java/com/alphawallet/app/ui/SwapActivity.java
index 7f001e3465..e07e102d7c 100644
--- a/app/src/main/java/com/alphawallet/app/ui/SwapActivity.java
+++ b/app/src/main/java/com/alphawallet/app/ui/SwapActivity.java
@@ -32,17 +32,17 @@
import com.alphawallet.app.entity.lifi.Chain;
import com.alphawallet.app.entity.lifi.Connection;
import com.alphawallet.app.entity.lifi.Quote;
-import com.alphawallet.app.entity.lifi.Token;
+import com.alphawallet.app.entity.lifi.LifiToken;
import com.alphawallet.app.ui.widget.entity.ActionSheetCallback;
import com.alphawallet.app.ui.widget.entity.ProgressInfo;
import com.alphawallet.app.util.BalanceUtils;
import com.alphawallet.app.util.SwapUtils;
import com.alphawallet.app.viewmodel.SwapViewModel;
-import com.alphawallet.app.viewmodel.Tokens;
+import com.alphawallet.app.util.LifiTokenUtils;
import com.alphawallet.app.web3.entity.Web3Transaction;
import com.alphawallet.app.widget.AWalletAlertDialog;
import com.alphawallet.app.widget.ActionSheetDialog;
-import com.alphawallet.app.widget.SelectTokenDialog;
+import com.alphawallet.app.widget.SelectLifiTokenDialog;
import com.alphawallet.app.widget.StandardHeader;
import com.alphawallet.app.widget.SwapSettingsDialog;
import com.alphawallet.app.widget.TokenInfoView;
@@ -65,8 +65,8 @@ public class SwapActivity extends BaseActivity implements StandardFunctionInterf
private SwapViewModel viewModel;
private TokenSelector sourceSelector;
private TokenSelector destSelector;
- private SelectTokenDialog sourceTokenDialog;
- private SelectTokenDialog destTokenDialog;
+ private SelectLifiTokenDialog sourceTokenDialog;
+ private SelectLifiTokenDialog destTokenDialog;
private ActionSheetDialog confirmationDialog;
private SwapSettingsDialog settingsDialog;
private AWalletAlertDialog progressDialog;
@@ -86,7 +86,7 @@ public class SwapActivity extends BaseActivity implements StandardFunctionInterf
private TextView chainName;
private com.alphawallet.app.entity.tokens.Token token;
private Wallet wallet;
- private Token sourceToken;
+ private LifiToken sourceToken;
private List chains;
private String selectedRouteProvider;
private CountDownTimer getQuoteTimer;
@@ -256,7 +256,7 @@ public void onAmountChanged(String amount)
}
@Override
- public void onSelectionChanged(Token token)
+ public void onSelectionChanged(LifiToken token)
{
sourceTokenChanged(token);
}
@@ -264,7 +264,7 @@ public void onSelectionChanged(Token token)
@Override
public void onMaxClicked()
{
- Token token = sourceSelector.getToken();
+ LifiToken token = sourceSelector.getToken();
if (token == null)
{
return;
@@ -293,7 +293,7 @@ public void onAmountChanged(String amount)
}
@Override
- public void onSelectionChanged(Token token)
+ public void onSelectionChanged(LifiToken token)
{
destTokenChanged(token);
}
@@ -342,7 +342,7 @@ private ActionSheetDialog createConfirmationAction(Quote quote)
return confDialog;
}
- private void destTokenChanged(Token token)
+ private void destTokenChanged(LifiToken token)
{
destSelector.setBalance(viewModel.getBalance(token));
@@ -355,7 +355,7 @@ private void destTokenChanged(Token token)
getAvailableRoutes();
}
- private void sourceTokenChanged(Token token)
+ private void sourceTokenChanged(LifiToken token)
{
if (destSelector.getToken() == null)
{
@@ -404,7 +404,7 @@ protected void onPause()
}
// The source token should default to the token selected in the main wallet dialog (ie the token from the intent).
- private void initSourceToken(Token selectedToken)
+ private void initSourceToken(LifiToken selectedToken)
{
if (selectedToken != null)
{
@@ -418,20 +418,20 @@ private void initSourceToken(Token selectedToken)
}
}
- private void initFromDialog(List fromTokens)
+ private void initFromDialog(List fromTokens)
{
- Tokens.sortValue(fromTokens);
- sourceTokenDialog = new SelectTokenDialog(fromTokens, this, tokenItem -> {
+ LifiTokenUtils.sortValue(fromTokens);
+ sourceTokenDialog = new SelectLifiTokenDialog(fromTokens, this, tokenItem -> {
sourceSelector.init(tokenItem);
sourceTokenDialog.dismiss();
});
}
- private void initToDialog(List toTokens)
+ private void initToDialog(List toTokens)
{
- Tokens.sortName(toTokens);
- Tokens.sortValue(toTokens);
- destTokenDialog = new SelectTokenDialog(toTokens, this, tokenItem -> {
+ LifiTokenUtils.sortName(toTokens);
+ LifiTokenUtils.sortValue(toTokens);
+ destTokenDialog = new SelectLifiTokenDialog(toTokens, this, tokenItem -> {
destSelector.init(tokenItem);
destTokenDialog.dismiss();
});
@@ -509,13 +509,13 @@ private void onConnections(List connections)
{
if (!connections.isEmpty())
{
- List fromTokens = new ArrayList<>();
- List toTokens = new ArrayList<>();
- Token selectedToken = null;
+ List fromTokens = new ArrayList<>();
+ List toTokens = new ArrayList<>();
+ LifiToken selectedToken = null;
for (Connection c : connections)
{
- for (Token t : c.fromTokens)
+ for (LifiToken t : c.fromTokens)
{
if (!fromTokens.contains(t))
{
@@ -534,7 +534,7 @@ private void onConnections(List connections)
}
}
- for (Token t : c.toTokens)
+ for (LifiToken t : c.toTokens)
{
if (!toTokens.contains(t))
{
diff --git a/app/src/main/java/com/alphawallet/app/ui/TransferRequestActivity.java b/app/src/main/java/com/alphawallet/app/ui/TransferRequestActivity.java
new file mode 100644
index 0000000000..692cd8791d
--- /dev/null
+++ b/app/src/main/java/com/alphawallet/app/ui/TransferRequestActivity.java
@@ -0,0 +1,603 @@
+package com.alphawallet.app.ui;
+
+import static com.alphawallet.app.widget.AWalletAlertDialog.ERROR;
+import static com.alphawallet.app.widget.AWalletAlertDialog.WARNING;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.text.TextUtils;
+import android.view.MenuItem;
+
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.alphawallet.app.C;
+import com.alphawallet.app.R;
+import com.alphawallet.app.analytics.Analytics;
+import com.alphawallet.app.entity.AnalyticsProperties;
+import com.alphawallet.app.entity.EIP681Type;
+import com.alphawallet.app.entity.ErrorEnvelope;
+import com.alphawallet.app.entity.GasEstimate;
+import com.alphawallet.app.entity.Operation;
+import com.alphawallet.app.entity.QRResult;
+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.tokens.Token;
+import com.alphawallet.app.service.GasService;
+import com.alphawallet.app.ui.widget.entity.ActionSheetCallback;
+import com.alphawallet.app.ui.widget.entity.AddressReadyCallback;
+import com.alphawallet.app.ui.widget.entity.AmountReadyCallback;
+import com.alphawallet.app.util.Utils;
+import com.alphawallet.app.viewmodel.SendViewModel;
+import com.alphawallet.app.web3.entity.Address;
+import com.alphawallet.app.web3.entity.Web3Transaction;
+import com.alphawallet.app.widget.AWalletAlertDialog;
+import com.alphawallet.app.widget.ActionSheetDialog;
+import com.alphawallet.app.widget.FunctionButtonBar;
+import com.alphawallet.app.widget.InputAddress;
+import com.alphawallet.app.widget.InputAmount;
+import com.alphawallet.app.widget.SignTransactionDialog;
+import com.alphawallet.hardware.SignatureFromKey;
+import com.alphawallet.token.tools.Convert;
+import com.alphawallet.token.tools.Numeric;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.SocketTimeoutException;
+import java.util.ArrayList;
+import java.util.Collections;
+
+import dagger.hilt.android.AndroidEntryPoint;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+import timber.log.Timber;
+
+@AndroidEntryPoint
+public class TransferRequestActivity extends BaseActivity implements
+ AmountReadyCallback,
+ StandardFunctionInterface,
+ AddressReadyCallback,
+ ActionSheetCallback
+{
+ private static final BigDecimal NEGATIVE = BigDecimal.ZERO.subtract(BigDecimal.ONE);
+ private final Handler handler = new Handler();
+ private SendViewModel viewModel;
+ private Wallet wallet;
+ private Token token;
+ private AWalletAlertDialog dialog;
+ private AWalletAlertDialog progressDialog;
+ private QRResult result;
+ private InputAmount amountInput;
+ private InputAddress addressInput;
+ private FunctionButtonBar functionBar;
+ private String sendAddress = null;
+ private String ensAddress;
+ private BigDecimal sendAmount = NEGATIVE;
+ private BigDecimal sendGasPrice = BigDecimal.ZERO;
+ private ActionSheetDialog confirmationDialog;
+ private final ActivityResultLauncher getGasSettings = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
+ result -> confirmationDialog.setCurrentGasIndex(result));
+ @Nullable
+ private Disposable calcGasCost;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_transfer_request);
+
+ toolbar();
+
+ setTitle(R.string.empty);
+
+ initViews();
+
+ initViewModel();
+
+ result = getIntent().getParcelableExtra(C.EXTRA_AMOUNT);
+
+ evaluateQrResult(result);
+ }
+
+ private void initViews()
+ {
+ amountInput = findViewById(R.id.input_amount);
+ addressInput = findViewById(R.id.input_address);
+ functionBar = findViewById(R.id.layoutButtons);
+ addressInput.setAddressCallback(this);
+ addressInput.setEditable(false);
+ addressInput.showControls(false);
+ amountInput.setEditable(false);
+ amountInput.showControls(false);
+
+ progressDialog = new AWalletAlertDialog(this);
+ progressDialog.setTitle(R.string.searching_for_token);
+ progressDialog.setIcon(AWalletAlertDialog.NONE);
+ progressDialog.setProgressMode();
+ progressDialog.setCancelable(false);
+ }
+
+ private void initViewModel()
+ {
+ viewModel = new ViewModelProvider(this).get(SendViewModel.class);
+ viewModel.wallet().observe(this, this::onWallet);
+ viewModel.finalisedToken().observe(this, this::onFinalisedToken);
+ viewModel.transactionFinalised().observe(this, this::txWritten);
+ viewModel.transactionError().observe(this, this::txError);
+ viewModel.error().observe(this, this::onError);
+ viewModel.progress().observe(this, this::showProgress);
+ }
+
+ private void evaluateQrResult(QRResult r)
+ {
+ if (r != null)
+ {
+ this.result = r;
+ viewModel.prepare();
+ }
+ else
+ {
+ displayScanError();
+ finish();
+ }
+ }
+
+ private void onWallet(Wallet wallet)
+ {
+ this.wallet = wallet;
+
+ long chainId = result.chainId;
+
+ if (!viewModel.isValidChain(chainId))
+ {
+ displayToast(getString(R.string.chain_not_support, String.valueOf(chainId)));
+ finish();
+ }
+ else if (!viewModel.isNetworkEnabled(chainId))
+ {
+ showChainChangeDialog();
+ }
+ else
+ {
+ findToken();
+ }
+ }
+
+ private void findToken()
+ {
+ if (result.type == EIP681Type.PAYMENT)
+ {
+ setTitle(getString(R.string.title_payment_request));
+ token = viewModel.getToken(result.chainId, wallet.address);
+ }
+ else if (result.type == EIP681Type.TRANSFER)
+ {
+ setTitle(getString(R.string.transfer_request));
+ token = viewModel.getToken(result.chainId, result.getAddress());
+ }
+
+ if (token != null)
+ {
+ evaluateToken(token);
+ }
+ else
+ {
+ // You don't have this token, attempt to fetch
+ showProgress(true);
+ viewModel.fetchToken(result.chainId, result.getAddress(), wallet.address);
+ }
+ }
+
+ private void evaluateToken(Token token)
+ {
+ if (token != null && token.balance.equals(BigDecimal.ZERO))
+ {
+ displayToast("Wallet does not have requested token");
+ showProgress(false);
+ finish();
+ }
+ else if (token != null && token.isERC20())
+ {
+ setupTokenContent(token);
+ addressInput.setAddress(result.functionToAddress);
+ amountInput.setAmount(result.tokenAmount.toString());
+ amountInput.getInputAmount();
+ }
+ else if (token != null && token.isEthereum())
+ {
+ setupTokenContent(token);
+ addressInput.setAddress(result.getAddress());
+ amountInput.setAmount(Convert.getConvertedValue(new BigDecimal(result.weiValue), Convert.Unit.ETHER.getFactor()));
+ amountInput.getInputAmount();
+ }
+ else // TODO: Handle NFT
+ {
+ displayToast("NFTs not supported yet.");
+ finish();
+ }
+ }
+
+ private void onFinalisedToken(Token token)
+ {
+ evaluateToken(token);
+ }
+
+ private void setupTokenContent(Token token)
+ {
+ this.token = token;
+ amountInput.setupToken(token, viewModel.getAssetDefinitionService(), viewModel.getTokenService(), this);
+ addressInput.setChainOverrideForWalletConnect(token.tokenInfo.chainId);
+ functionBar.revealButtons();
+ functionBar.setupFunctions(this, new ArrayList<>(Collections.singletonList(R.string.action_next)));
+ viewModel.startGasCycle(token.tokenInfo.chainId);
+ }
+
+ private void onError(ErrorEnvelope errorEnvelope)
+ {
+ displayToast(errorEnvelope.message);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item)
+ {
+ if (item.getItemId() == android.R.id.home)
+ {
+ onBackPressed();
+ }
+ return false;
+ }
+
+ @Override
+ public void onBackPressed()
+ {
+ finish();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data)
+ {
+ {
+ Operation taskCode = null;
+ if (requestCode >= SignTransactionDialog.REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS && requestCode <= SignTransactionDialog.REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS + 10)
+ {
+ taskCode = Operation.values()[requestCode - SignTransactionDialog.REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS];
+ requestCode = SignTransactionDialog.REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS;
+ }
+ 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);
+ }
+ else
+ {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+ }
+
+ private void calculateEstimateDialog()
+ {
+ if (dialog != null && dialog.isShowing()) dialog.dismiss();
+ dialog = new AWalletAlertDialog(this);
+ dialog.setTitle(getString(R.string.calc_gas_limit));
+ dialog.setIcon(AWalletAlertDialog.NONE);
+ dialog.setProgressMode();
+ dialog.setCancelable(false);
+ dialog.show();
+ }
+
+ private void displayScanError()
+ {
+ if (dialog != null && dialog.isShowing()) dialog.dismiss();
+ dialog = new AWalletAlertDialog(this);
+ dialog.setIcon(AWalletAlertDialog.ERROR);
+ dialog.setTitle(R.string.toast_qr_code_no_address);
+ dialog.setButtonText(R.string.dialog_cancel_back);
+ dialog.setButtonListener(v -> dialog.dismiss());
+ dialog.setCanceledOnTouchOutside(false);
+ dialog.show();
+ }
+
+ private void displayError(int titleId, String message)
+ {
+ if (dialog != null && dialog.isShowing()) dialog.dismiss();
+ dialog = new AWalletAlertDialog(this);
+ dialog.setIcon(AWalletAlertDialog.ERROR);
+ dialog.setTitle(titleId);
+ dialog.setMessage(message);
+ dialog.setButtonText(R.string.dialog_ok);
+ dialog.setButtonListener(v -> dialog.dismiss());
+ dialog.show();
+ }
+
+ @Override
+ protected void onDestroy()
+ {
+ if (dialog != null && dialog.isShowing())
+ {
+ dialog.dismiss();
+ }
+ super.onDestroy();
+ if (viewModel != null) viewModel.onDestroy();
+ if (handler != null) handler.removeCallbacksAndMessages(null);
+ if (amountInput != null) amountInput.onDestroy();
+ if (confirmationDialog != null) confirmationDialog.onDestroy();
+ }
+
+ @Override
+ public void amountReady(BigDecimal value, BigDecimal gasPrice)
+ {
+ if (isBalanceSufficient(value))
+ {
+ sendAmount = value;
+ sendGasPrice = gasPrice;
+ calculateTransactionCost();
+ }
+ else
+ {
+ sendAmount = NEGATIVE;
+ amountInput.showError(true, 0);
+ addressInput.stopNameCheck();
+ }
+ }
+
+ private boolean isBalanceSufficient(BigDecimal value)
+ {
+ return (token.isEthereum() && token.balance.subtract(value).compareTo(BigDecimal.ZERO) > 0) // if sending base ethereum then check we have more than just the value
+ || (token.getBalanceRaw().subtract(value).compareTo(BigDecimal.ZERO) >= 0);
+ }
+
+ @Override
+ public void handleClick(String action, int actionId)
+ {
+ if (actionId == R.string.action_next)
+ {
+ amountInput.getInputAmount();
+ addressInput.getAddress();
+ }
+ }
+
+ @Override
+ public void addressReady(String address, String ensName)
+ {
+ sendAddress = address;
+ ensAddress = ensName;
+ if (!Utils.isAddressValid(address))
+ {
+ //show address error
+ addressInput.setError(getString(R.string.error_invalid_address));
+ }
+ else
+ {
+ calculateTransactionCost();
+ }
+ }
+
+ private void calculateTransactionCost()
+ {
+ if ((calcGasCost != null && !calcGasCost.isDisposed()) ||
+ (confirmationDialog != null && confirmationDialog.isShowing())) return;
+
+ if (sendAmount.compareTo(NEGATIVE) > 0 && Utils.isAddressValid(sendAddress))
+ {
+ final String txSendAddress = sendAddress;
+ sendAddress = null;
+ //either sending base chain or ERC20 tokens.
+ final byte[] transactionBytes = viewModel.getTransactionBytes(token, txSendAddress, sendAmount);
+
+ final String txDestAddress = token.isEthereum() ? txSendAddress : token.getAddress(); //either another address, or ERC20 Token address
+
+ calculateEstimateDialog();
+ //form payload and calculate tx cost
+ calcGasCost = viewModel.calculateGasEstimate(wallet, transactionBytes, token.tokenInfo.chainId, txDestAddress, BigDecimal.ZERO)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(estimate -> checkConfirm(estimate, transactionBytes, txDestAddress, txSendAddress),
+ error -> handleError(error, transactionBytes, token.getAddress(), txSendAddress));
+ }
+ }
+
+ private void handleError(Throwable throwable, final byte[] transactionBytes, final String txSendAddress, final String resolvedAddress)
+ {
+ if (dialog != null && dialog.isShowing()) dialog.dismiss();
+ displayErrorMessage(throwable.getMessage());
+ }
+
+ private void checkConfirm(GasEstimate estimate, final byte[] transactionBytes, final String txSendAddress, final String resolvedAddress)
+ {
+ BigInteger ethValue = token.isEthereum() ? sendAmount.toBigInteger() : BigInteger.ZERO;
+ long leafCode = amountInput.isSendAll() ? -2 : -1;
+ Web3Transaction w3tx = new Web3Transaction(
+ new Address(txSendAddress),
+ token.isEthereum() ? null : new Address(token.getAddress()),
+ new Address(wallet.address),
+ ethValue,
+ sendGasPrice.toBigInteger(),
+ estimate.getValue(),
+ -1,
+ Numeric.toHexString(transactionBytes),
+ leafCode);
+
+ if (estimate.hasError() || estimate.getValue().equals(BigInteger.ZERO))
+ {
+ estimateError(estimate, w3tx, transactionBytes, txSendAddress, resolvedAddress);
+ }
+ else
+ {
+ if (dialog != null && dialog.isShowing()) dialog.dismiss();
+ confirmationDialog = new ActionSheetDialog(this, w3tx, token, ensAddress,
+ resolvedAddress, viewModel.getTokenService(), this);
+ confirmationDialog.setCanceledOnTouchOutside(false);
+ confirmationDialog.show();
+ sendAmount = NEGATIVE;
+ }
+ }
+
+ @Override
+ public void getAuthorisation(SignAuthenticationCallback callback)
+ {
+ viewModel.getAuthentication(this, wallet, callback);
+ }
+
+ @Override
+ public void sendTransaction(Web3Transaction finalTx)
+ {
+ viewModel.requestSignature(finalTx, wallet, token.tokenInfo.chainId);
+ }
+
+ @Override
+ public void completeSendTransaction(Web3Transaction tx, SignatureFromKey signature)
+ {
+ viewModel.sendTransaction(wallet, token.tokenInfo.chainId, tx, signature);
+ }
+
+ @Override
+ public void dismissed(String txHash, long callbackId, boolean actionCompleted)
+ {
+ if (!TextUtils.isEmpty(txHash))
+ {
+ Intent intent = new Intent();
+ intent.putExtra(C.EXTRA_TXHASH, txHash);
+ setResult(RESULT_OK, intent);
+
+ finish();
+ }
+ }
+
+ @Override
+ public ActivityResultLauncher gasSelectLauncher()
+ {
+ return getGasSettings;
+ }
+
+ @Override
+ public void notifyConfirm(String mode)
+ {
+ AnalyticsProperties props = new AnalyticsProperties();
+ props.put(Analytics.PROPS_ACTION_SHEET_MODE, mode);
+ viewModel.track(Analytics.Action.ACTION_SHEET_COMPLETED, props);
+ }
+
+ @Override
+ public WalletType getWalletType()
+ {
+ return wallet.type;
+ }
+
+ private void txWritten(TransactionReturn txData)
+ {
+ confirmationDialog.transactionWritten(txData.hash);
+ viewModel.setLastSentToken(token);
+ }
+
+ private void txError(TransactionReturn txError)
+ {
+ Timber.e(txError.throwable);
+ if (txError.throwable instanceof SocketTimeoutException)
+ {
+ showTxnTimeoutDialog();
+ }
+ else
+ {
+ showTxnErrorDialog(txError.throwable);
+ }
+ confirmationDialog.dismiss();
+ }
+
+ private void estimateError(GasEstimate estimate, final Web3Transaction w3tx, final byte[] transactionBytes, final String txSendAddress, final String resolvedAddress)
+ {
+ if (dialog != null && dialog.isShowing()) dialog.dismiss();
+ dialog = new AWalletAlertDialog(this);
+ dialog.setIcon(WARNING);
+ dialog.setTitle(estimate.hasError() ?
+ R.string.dialog_title_gas_estimation_failed :
+ R.string.confirm_transaction
+ );
+ String message = estimate.hasError() ?
+ getString(R.string.dialog_message_gas_estimation_failed, estimate.getError()) :
+ getString(R.string.error_transaction_may_fail);
+ dialog.setMessage(message);
+ dialog.setButtonText(R.string.action_proceed);
+ dialog.setSecondaryButtonText(R.string.action_cancel);
+ dialog.setButtonListener(v -> {
+ BigInteger gasEstimate = GasService.getDefaultGasLimit(token, w3tx);
+ checkConfirm(new GasEstimate(gasEstimate), transactionBytes, txSendAddress, resolvedAddress);
+ });
+
+ dialog.setSecondaryButtonListener(v -> {
+ dialog.dismiss();
+ });
+
+ dialog.show();
+ }
+
+ void showTxnErrorDialog(Throwable t)
+ {
+ if (dialog != null && dialog.isShowing()) dialog.dismiss();
+ dialog = new AWalletAlertDialog(this);
+ dialog.setIcon(ERROR);
+ dialog.setTitle(R.string.error_transaction_failed);
+ dialog.setMessage(t.getMessage());
+ dialog.setButtonText(R.string.button_ok);
+ dialog.setButtonListener(v -> {
+ dialog.dismiss();
+ });
+ dialog.show();
+ }
+
+ void showTxnTimeoutDialog()
+ {
+ if (dialog != null && dialog.isShowing()) dialog.dismiss();
+ dialog = new AWalletAlertDialog(this);
+ dialog.setIcon(WARNING);
+ dialog.setTitle(R.string.error_transaction_timeout);
+ dialog.setMessage(R.string.message_transaction_timeout);
+ dialog.setButton(R.string.ok, v -> {
+ dialog.dismiss();
+ });
+ dialog.show();
+ }
+
+ private void showChainChangeDialog()
+ {
+ if (dialog != null && dialog.isShowing()) dialog.dismiss();
+ dialog = new AWalletAlertDialog(this);
+ dialog.setIcon(AWalletAlertDialog.WARNING);
+ dialog.setTitle(R.string.change_chain_request);
+ dialog.setMessage(R.string.change_chain_message);
+ dialog.setButtonText(R.string.dialog_ok);
+ dialog.setButtonListener(v -> {
+ findToken();
+ dialog.dismiss();
+ });
+ dialog.setSecondaryButtonText(R.string.action_cancel);
+ dialog.setSecondaryButtonListener(v -> {
+ dialog.dismiss();
+ finish();
+ });
+ dialog.show();
+ }
+
+ private void showProgress(Boolean showProgress)
+ {
+ if (progressDialog != null)
+ {
+ if (showProgress)
+ {
+ progressDialog.show();
+ }
+ else
+ {
+ progressDialog.dismiss();
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/alphawallet/app/ui/widget/adapter/SelectLifiTokenAdapter.java b/app/src/main/java/com/alphawallet/app/ui/widget/adapter/SelectLifiTokenAdapter.java
new file mode 100644
index 0000000000..84f4145dc9
--- /dev/null
+++ b/app/src/main/java/com/alphawallet/app/ui/widget/adapter/SelectLifiTokenAdapter.java
@@ -0,0 +1,120 @@
+package com.alphawallet.app.ui.widget.adapter;
+
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.alphawallet.app.R;
+import com.alphawallet.app.entity.lifi.LifiToken;
+import com.alphawallet.app.util.LifiTokenFilter;
+import com.alphawallet.app.widget.AddressIcon;
+import com.alphawallet.app.widget.SelectLifiTokenDialog;
+import com.google.android.material.radiobutton.MaterialRadioButton;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SelectLifiTokenAdapter extends RecyclerView.Adapter
+{
+ private final List displayData;
+ private final SelectLifiTokenDialog.EventListener callback;
+ private final LifiTokenFilter lifiTokenFilter;
+ private String selectedTokenAddress;
+
+ public SelectLifiTokenAdapter(List tokens, SelectLifiTokenDialog.EventListener callback)
+ {
+ lifiTokenFilter = new LifiTokenFilter(tokens);
+ this.callback = callback;
+ displayData = new ArrayList<>();
+ displayData.addAll(tokens);
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
+ {
+ int buttonTypeId = R.layout.item_token_select;
+ View itemView = LayoutInflater.from(parent.getContext())
+ .inflate(buttonTypeId, parent, false);
+ return new ViewHolder(itemView);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder holder, int position)
+ {
+ LifiToken item = displayData.get(position);
+ if (item != null)
+ {
+ holder.name.setText(item.name);
+ holder.name.append(" (");
+ holder.name.append(item.symbol);
+ holder.name.append(")");
+
+ holder.tokenIcon.bindData(item.logoURI, item.chainId, selectedTokenAddress, item.symbol);
+
+ String balance = item.balance;
+ if (!TextUtils.isEmpty(balance))
+ {
+ holder.balance.setText(balance);
+ holder.balance.append(" ");
+ }
+ else
+ {
+ holder.balance.setText("0 ");
+ }
+
+ holder.radio.setChecked(item.address.equalsIgnoreCase(selectedTokenAddress));
+ holder.balance.append(item.symbol);
+
+ holder.itemLayout.setOnClickListener(v -> callback.onChainSelected(item));
+ }
+ }
+
+ public void filter(String keyword)
+ {
+ updateList(lifiTokenFilter.filterBy(keyword));
+ }
+
+ public void updateList(List filteredList)
+ {
+ displayData.clear();
+ displayData.addAll(filteredList);
+ notifyDataSetChanged();
+ }
+
+ public void setSelectedToken(String address)
+ {
+ selectedTokenAddress = address;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getItemCount()
+ {
+ return displayData.size();
+ }
+
+ static class ViewHolder extends RecyclerView.ViewHolder
+ {
+ MaterialRadioButton radio;
+ TextView name;
+ TextView balance;
+ View itemLayout;
+ AddressIcon tokenIcon;
+
+ ViewHolder(View view)
+ {
+ super(view);
+ radio = view.findViewById(R.id.radio);
+ name = view.findViewById(R.id.name);
+ balance = view.findViewById(R.id.balance);
+ itemLayout = view.findViewById(R.id.layout_list_item);
+ tokenIcon = view.findViewById(R.id.token_icon);
+ }
+ }
+}
diff --git a/app/src/main/java/com/alphawallet/app/ui/widget/adapter/SelectTokenAdapter.java b/app/src/main/java/com/alphawallet/app/ui/widget/adapter/SelectTokenAdapter.java
index c5f32d6305..03ebdb99cc 100644
--- a/app/src/main/java/com/alphawallet/app/ui/widget/adapter/SelectTokenAdapter.java
+++ b/app/src/main/java/com/alphawallet/app/ui/widget/adapter/SelectTokenAdapter.java
@@ -1,5 +1,6 @@
package com.alphawallet.app.ui.widget.adapter;
+import android.annotation.SuppressLint;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
@@ -10,10 +11,10 @@
import androidx.recyclerview.widget.RecyclerView;
import com.alphawallet.app.R;
-import com.alphawallet.app.entity.lifi.Token;
-import com.alphawallet.app.widget.AddressIcon;
+import com.alphawallet.app.entity.tokens.Token;
+import com.alphawallet.app.util.TokenFilter;
import com.alphawallet.app.widget.SelectTokenDialog;
-import com.google.android.material.radiobutton.MaterialRadioButton;
+import com.alphawallet.app.widget.TokenIcon;
import java.util.ArrayList;
import java.util.List;
@@ -21,14 +22,13 @@
public class SelectTokenAdapter extends RecyclerView.Adapter
{
private final List displayData;
- private final SelectTokenDialog.SelectTokenDialogEventListener callback;
private final TokenFilter tokenFilter;
- private String selectedTokenAddress;
+ private final SelectTokenDialog.OnTokenClickListener listener;
- public SelectTokenAdapter(List tokens, SelectTokenDialog.SelectTokenDialogEventListener callback)
+ public SelectTokenAdapter(List tokens, SelectTokenDialog.OnTokenClickListener listener)
{
+ this.listener = listener;
tokenFilter = new TokenFilter(tokens);
- this.callback = callback;
displayData = new ArrayList<>();
displayData.addAll(tokens);
}
@@ -37,26 +37,22 @@ public SelectTokenAdapter(List tokens, SelectTokenDialog.SelectTokenDialo
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
{
- int buttonTypeId = R.layout.item_token_select;
View itemView = LayoutInflater.from(parent.getContext())
- .inflate(buttonTypeId, parent, false);
+ .inflate(R.layout.item_select_token, parent, false);
return new ViewHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position)
{
- Token item = displayData.get(position);
- if (item != null)
+ holder.setIsRecyclable(false);
+ Token token = displayData.get(position);
+ if (token != null)
{
- holder.name.setText(item.name);
- holder.name.append(" (");
- holder.name.append(item.symbol);
- holder.name.append(")");
+ holder.name.setText(token.tokenInfo.name);
+ holder.tokenIcon.bindData(token);
- holder.tokenIcon.bindData(item.logoURI, item.chainId, selectedTokenAddress, item.symbol);
-
- String balance = item.balance;
+ String balance = token.getStringBalanceForUI(token.tokenInfo.decimals);
if (!TextUtils.isEmpty(balance))
{
holder.balance.setText(balance);
@@ -67,10 +63,9 @@ public void onBindViewHolder(@NonNull ViewHolder holder, int position)
holder.balance.setText("0 ");
}
- holder.radio.setChecked(item.address.equalsIgnoreCase(selectedTokenAddress));
- holder.balance.append(item.symbol);
+ holder.balance.append(token.getSymbol());
- holder.itemLayout.setOnClickListener(v -> callback.onChainSelected(item));
+ holder.itemLayout.setOnClickListener(v -> listener.onTokenClicked(token));
}
}
@@ -79,6 +74,7 @@ public void filter(String keyword)
updateList(tokenFilter.filterBy(keyword));
}
+ @SuppressLint("NotifyDataSetChanged")
public void updateList(List filteredList)
{
displayData.clear();
@@ -86,12 +82,6 @@ public void updateList(List filteredList)
notifyDataSetChanged();
}
- public void setSelectedToken(String address)
- {
- selectedTokenAddress = address;
- notifyDataSetChanged();
- }
-
@Override
public int getItemCount()
{
@@ -100,16 +90,14 @@ public int getItemCount()
static class ViewHolder extends RecyclerView.ViewHolder
{
- MaterialRadioButton radio;
TextView name;
TextView balance;
View itemLayout;
- AddressIcon tokenIcon;
+ TokenIcon tokenIcon;
ViewHolder(View view)
{
super(view);
- radio = view.findViewById(R.id.radio);
name = view.findViewById(R.id.name);
balance = view.findViewById(R.id.balance);
itemLayout = view.findViewById(R.id.layout_list_item);
diff --git a/app/src/main/java/com/alphawallet/app/ui/widget/adapter/TokenFilter.java b/app/src/main/java/com/alphawallet/app/util/LifiTokenFilter.java
similarity index 74%
rename from app/src/main/java/com/alphawallet/app/ui/widget/adapter/TokenFilter.java
rename to app/src/main/java/com/alphawallet/app/util/LifiTokenFilter.java
index 1e9fb39653..34b1e59e1c 100644
--- a/app/src/main/java/com/alphawallet/app/ui/widget/adapter/TokenFilter.java
+++ b/app/src/main/java/com/alphawallet/app/util/LifiTokenFilter.java
@@ -1,21 +1,21 @@
-package com.alphawallet.app.ui.widget.adapter;
+package com.alphawallet.app.util;
import android.text.TextUtils;
import androidx.annotation.NonNull;
-import com.alphawallet.app.entity.lifi.Token;
+import com.alphawallet.app.entity.lifi.LifiToken;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
-public class TokenFilter
+public class LifiTokenFilter
{
- private final List tokens;
+ private final List tokens;
- public TokenFilter(List tokens)
+ public LifiTokenFilter(List tokens)
{
this.tokens = tokens;
removeBadTokens();
@@ -23,10 +23,10 @@ public TokenFilter(List tokens)
private void removeBadTokens()
{
- ListIterator iterator = this.tokens.listIterator();
+ ListIterator iterator = this.tokens.listIterator();
while (iterator.hasNext())
{
- Token t = iterator.next();
+ LifiToken t = iterator.next();
if (TextUtils.isEmpty(t.name) || TextUtils.isEmpty(t.symbol))
{
iterator.remove();
@@ -34,13 +34,13 @@ private void removeBadTokens()
}
}
- public List filterBy(String keyword)
+ public List filterBy(String keyword)
{
String lowerCaseKeyword = lowerCase(keyword);
- List result = new ArrayList<>();
+ List result = new ArrayList<>();
// First filter: Add all entries that start with the keyword on top of the list.
- for (Token lToken : this.tokens)
+ for (LifiToken lToken : this.tokens)
{
String name = lowerCase(lToken.name);
String symbol = lowerCase(lToken.symbol);
@@ -52,7 +52,7 @@ public List filterBy(String keyword)
}
// Second filter: Add the rest of the entries that contain the keyword on top of the list.
- for (Token lToken : this.tokens)
+ for (LifiToken lToken : this.tokens)
{
String name = lowerCase(lToken.name);
String symbol = lowerCase(lToken.symbol);
diff --git a/app/src/main/java/com/alphawallet/app/viewmodel/Tokens.java b/app/src/main/java/com/alphawallet/app/util/LifiTokenUtils.java
similarity index 80%
rename from app/src/main/java/com/alphawallet/app/viewmodel/Tokens.java
rename to app/src/main/java/com/alphawallet/app/util/LifiTokenUtils.java
index 07689b8646..84809670bb 100644
--- a/app/src/main/java/com/alphawallet/app/viewmodel/Tokens.java
+++ b/app/src/main/java/com/alphawallet/app/util/LifiTokenUtils.java
@@ -1,14 +1,15 @@
-package com.alphawallet.app.viewmodel;
+package com.alphawallet.app.util;
-import com.alphawallet.app.entity.lifi.Token;
+
+import com.alphawallet.app.entity.lifi.LifiToken;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
-public class Tokens
+public class LifiTokenUtils
{
- public static void sortValue(List tokenItems)
+ public static void sortValue(List tokenItems)
{
Collections.sort(tokenItems, (l, r) -> {
if (l.isNativeToken())
@@ -28,7 +29,7 @@ else if (r.isNativeToken())
});
}
- public static void sortName(List tokenItems)
+ public static void sortName(List tokenItems)
{
Collections.sort(tokenItems, (l, r) -> {
if (l.isNativeToken())
diff --git a/app/src/main/java/com/alphawallet/app/util/TokenFilter.java b/app/src/main/java/com/alphawallet/app/util/TokenFilter.java
new file mode 100644
index 0000000000..4664f4f4b4
--- /dev/null
+++ b/app/src/main/java/com/alphawallet/app/util/TokenFilter.java
@@ -0,0 +1,59 @@
+package com.alphawallet.app.util;
+
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+
+import com.alphawallet.app.entity.tokens.Token;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+public class TokenFilter
+{
+ private final List tokens;
+
+ private final List copy;
+
+ public TokenFilter(List tokens)
+ {
+ this.tokens = tokens;
+ this.copy = new ArrayList<>();
+ this.copy.addAll(tokens);
+ }
+
+ public List filterBy(String keyword)
+ {
+ if (TextUtils.isEmpty(keyword)) return copy;
+
+ String lowerCaseKeyword = lowerCase(keyword);
+
+ List result = new ArrayList<>();
+ for (Token t : this.tokens)
+ {
+ String name = lowerCase(t.getFullName());
+ String symbol = lowerCase(t.getSymbol());
+
+ if (name.startsWith(lowerCaseKeyword) || symbol.startsWith(lowerCaseKeyword))
+ {
+ result.add(0, t);
+ }
+ else if (name.contains(lowerCaseKeyword) || symbol.contains(lowerCaseKeyword))
+ {
+ if (!result.contains(t))
+ {
+ result.add(t);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ @NonNull
+ private String lowerCase(String name)
+ {
+ return name.toLowerCase(Locale.ENGLISH);
+ }
+}
diff --git a/app/src/main/java/com/alphawallet/app/viewmodel/AddTokenViewModel.java b/app/src/main/java/com/alphawallet/app/viewmodel/AddTokenViewModel.java
index b821d5b5be..3cd9c3d09c 100644
--- a/app/src/main/java/com/alphawallet/app/viewmodel/AddTokenViewModel.java
+++ b/app/src/main/java/com/alphawallet/app/viewmodel/AddTokenViewModel.java
@@ -1,5 +1,6 @@
package com.alphawallet.app.viewmodel;
+import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
@@ -20,10 +21,10 @@
import com.alphawallet.app.interact.FetchTransactionsInteract;
import com.alphawallet.app.interact.GenericWalletInteract;
import com.alphawallet.app.repository.EthereumNetworkRepositoryType;
+import com.alphawallet.app.router.TransferRequestRouter;
import com.alphawallet.app.service.AssetDefinitionService;
import com.alphawallet.app.service.TokensService;
import com.alphawallet.app.ui.ImportTokenActivity;
-import com.alphawallet.app.ui.SendActivity;
import java.math.BigDecimal;
import java.util.ArrayList;
@@ -208,28 +209,9 @@ public void prepare()
findWallet();
}
- public void showSend(Context ctx, QRResult result, Token token)
+ public void showSend(Activity ctx, QRResult result)
{
- Intent intent = new Intent(ctx, SendActivity.class);
- boolean sendingTokens = (result.getFunction() != null && result.getFunction().length() > 0);
- String address = wallet.getValue().address;
- int decimals = 18;
-
- if (sendingTokens)
- {
- address = result.getAddress();
- decimals = token.tokenInfo.decimals;
- }
-
- intent.putExtra(C.EXTRA_SENDING_TOKENS, sendingTokens);
- intent.putExtra(C.EXTRA_CONTRACT_ADDRESS, address);
- intent.putExtra(C.EXTRA_NETWORKID, token.tokenInfo.chainId);
- intent.putExtra(C.EXTRA_SYMBOL, ethereumNetworkRepository.getNetworkByChain(result.chainId).symbol);
- intent.putExtra(C.EXTRA_DECIMALS, decimals);
- intent.putExtra(C.Key.WALLET, wallet.getValue());
- intent.putExtra(C.EXTRA_AMOUNT, result);
- intent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- ctx.startActivity(intent);
+ new TransferRequestRouter().open(ctx, result);
}
private List getNetworkIds()
diff --git a/app/src/main/java/com/alphawallet/app/viewmodel/BaseNavigationActivity.java b/app/src/main/java/com/alphawallet/app/viewmodel/BaseNavigationActivity.java
index 0b215c0e59..0a67274150 100644
--- a/app/src/main/java/com/alphawallet/app/viewmodel/BaseNavigationActivity.java
+++ b/app/src/main/java/com/alphawallet/app/viewmodel/BaseNavigationActivity.java
@@ -1,10 +1,13 @@
package com.alphawallet.app.viewmodel;
+import android.content.Intent;
import android.view.View;
import com.alphawallet.app.R;
import com.alphawallet.app.entity.WalletPage;
+import com.alphawallet.app.router.SendTokenRouter;
import com.alphawallet.app.ui.BaseActivity;
+import com.alphawallet.app.ui.SendActivity;
import com.alphawallet.app.widget.AWalletBottomNavigationView;
public class BaseNavigationActivity extends BaseActivity implements AWalletBottomNavigationView.OnBottomNavigationItemSelectedListener {
@@ -13,6 +16,9 @@ public class BaseNavigationActivity extends BaseActivity implements AWalletBotto
protected void initBottomNavigation() {
nav = findViewById(R.id.nav);
nav.setListener(this);
+ nav.setSendButtonListener(v -> {
+ new SendTokenRouter().open(this);
+ });
}
protected void selectNavigationItem(WalletPage position) {
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 a386d0a51d..9fb42649db 100644
--- a/app/src/main/java/com/alphawallet/app/viewmodel/DappBrowserViewModel.java
+++ b/app/src/main/java/com/alphawallet/app/viewmodel/DappBrowserViewModel.java
@@ -31,6 +31,7 @@
import com.alphawallet.app.interact.CreateTransactionInteract;
import com.alphawallet.app.interact.GenericWalletInteract;
import com.alphawallet.app.repository.EthereumNetworkRepositoryType;
+import com.alphawallet.app.router.TransferRequestRouter;
import com.alphawallet.app.service.AnalyticsServiceType;
import com.alphawallet.app.service.AssetDefinitionService;
import com.alphawallet.app.service.GasService;
@@ -42,7 +43,6 @@
import com.alphawallet.app.ui.ImportTokenActivity;
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;
@@ -267,22 +267,9 @@ public void failedAuthentication(Operation signData)
keyService.failedAuthentication(signData);
}
- public void showSend(Context ctx, QRResult result)
+ public void showSend(Activity ctx, QRResult result)
{
- Intent intent = new Intent(ctx, SendActivity.class);
- boolean sendingTokens = (result.getFunction() != null && result.getFunction().length() > 0);
- String address = defaultWallet.getValue().address;
- int decimals = 18;
-
- intent.putExtra(C.EXTRA_SENDING_TOKENS, sendingTokens);
- intent.putExtra(C.EXTRA_CONTRACT_ADDRESS, address);
- intent.putExtra(C.EXTRA_NETWORKID, result.chainId);
- intent.putExtra(C.EXTRA_SYMBOL, ethereumNetworkRepository.getNetworkByChain(result.chainId).symbol);
- intent.putExtra(C.EXTRA_DECIMALS, decimals);
- intent.putExtra(C.Key.WALLET, defaultWallet.getValue());
- intent.putExtra(C.EXTRA_AMOUNT, result);
- intent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- ctx.startActivity(intent);
+ new TransferRequestRouter().open(ctx, result);
}
public void requestSignature(Web3Transaction finalTx, Wallet wallet, long chainId)
diff --git a/app/src/main/java/com/alphawallet/app/viewmodel/Erc20DetailViewModel.java b/app/src/main/java/com/alphawallet/app/viewmodel/Erc20DetailViewModel.java
index 1a7fe54bd7..a7bff4ff4d 100644
--- a/app/src/main/java/com/alphawallet/app/viewmodel/Erc20DetailViewModel.java
+++ b/app/src/main/java/com/alphawallet/app/viewmodel/Erc20DetailViewModel.java
@@ -101,12 +101,11 @@ public AssetDefinitionService getAssetDefinitionService()
return this.assetDefinitionService;
}
- public void showSendToken(Activity act, Wallet wallet, Token token)
+ public void showSendToken(Activity act, Token token)
{
if (token != null)
{
- new SendTokenRouter().open(act, wallet.address, token.getSymbol(), token.tokenInfo.decimals,
- wallet, token, token.tokenInfo.chainId);
+ new SendTokenRouter().open(act, token);
}
}
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 dcd4ab4cee..1de7d7ee54 100644
--- a/app/src/main/java/com/alphawallet/app/viewmodel/HomeViewModel.java
+++ b/app/src/main/java/com/alphawallet/app/viewmodel/HomeViewModel.java
@@ -49,10 +49,12 @@
import com.alphawallet.app.repository.PreferenceRepositoryType;
import com.alphawallet.app.repository.TokenRepository;
import com.alphawallet.app.repository.entity.RealmWCSession;
+import com.alphawallet.app.router.TransferRequestRouter;
import com.alphawallet.app.router.ExternalBrowserRouter;
import com.alphawallet.app.router.ImportTokenRouter;
import com.alphawallet.app.router.MyAddressRouter;
import com.alphawallet.app.service.AlphaWalletNotificationService;
+import com.alphawallet.app.router.SendTokenRouter;
import com.alphawallet.app.service.AnalyticsServiceType;
import com.alphawallet.app.service.AssetDefinitionService;
import com.alphawallet.app.service.RealmManager;
@@ -62,7 +64,6 @@
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.util.QRParser;
import com.alphawallet.app.util.RateApp;
import com.alphawallet.app.util.Utils;
@@ -379,8 +380,7 @@ public void handleQRCode(Activity activity, String qrCode)
case TRANSFER:
props.put(QrScanResultType.KEY, QrScanResultType.ADDRESS_OR_EIP_681.getValue());
track(Analytics.Action.SCAN_QR_CODE_SUCCESS, props);
-
- showSend(activity, qrResult);
+ new TransferRequestRouter().open(activity, qrResult);
break;
case FUNCTION_CALL:
props.put(QrScanResultType.KEY, QrScanResultType.ADDRESS_OR_EIP_681.getValue());
@@ -425,7 +425,7 @@ private void showActionSheet(Activity activity, QRResult qrResult)
View.OnClickListener listener = v -> {
if (v.getId() == R.id.send_to_this_address_action)
{
- showSend(activity, qrResult);
+ new SendTokenRouter().open(activity, qrResult.getAddress());
}
else if (v.getId() == R.id.add_custom_token_action)
{
@@ -481,24 +481,6 @@ else if (v.getId() == R.id.close_action)
dialog.show();
}
- public void showSend(Activity ctx, QRResult result)
- {
- Intent intent = new Intent(ctx, SendActivity.class);
- boolean sendingTokens = (result.getFunction() != null && result.getFunction().length() > 0);
- String address = defaultWallet.getValue().address;
- int decimals = 18;
-
- intent.putExtra(C.EXTRA_SENDING_TOKENS, sendingTokens);
- intent.putExtra(C.EXTRA_CONTRACT_ADDRESS, address);
- intent.putExtra(C.EXTRA_NETWORKID, result.chainId);
- intent.putExtra(C.EXTRA_SYMBOL, ethereumNetworkRepository.getNetworkByChain(result.chainId).symbol);
- intent.putExtra(C.EXTRA_DECIMALS, decimals);
- intent.putExtra(C.Key.WALLET, defaultWallet.getValue());
- intent.putExtra(C.EXTRA_AMOUNT, result);
- intent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- ctx.startActivity(intent);
- }
-
public void showMyAddress(Activity activity)
{
myAddressRouter.open(activity, defaultWallet.getValue());
diff --git a/app/src/main/java/com/alphawallet/app/viewmodel/NFTInfoViewModel.java b/app/src/main/java/com/alphawallet/app/viewmodel/NFTInfoViewModel.java
index 3d6ad92f79..85f07aa211 100644
--- a/app/src/main/java/com/alphawallet/app/viewmodel/NFTInfoViewModel.java
+++ b/app/src/main/java/com/alphawallet/app/viewmodel/NFTInfoViewModel.java
@@ -65,15 +65,6 @@ public AssetDefinitionService getAssetDefinitionService()
return this.assetDefinitionService;
}
- public void showSendToken(Activity act, Wallet wallet, Token token)
- {
- if (token != null)
- {
- new SendTokenRouter().open(act, wallet.address, token.getSymbol(), token.tokenInfo.decimals,
- wallet, token, token.tokenInfo.chainId);
- }
- }
-
public void checkTokenScriptValidity(Token token)
{
disposable = assetDefinitionService.getSignatureData(token.tokenInfo.chainId, token.tokenInfo.address)
diff --git a/app/src/main/java/com/alphawallet/app/viewmodel/NFTViewModel.java b/app/src/main/java/com/alphawallet/app/viewmodel/NFTViewModel.java
index 7d4f1095d2..76a7606729 100644
--- a/app/src/main/java/com/alphawallet/app/viewmodel/NFTViewModel.java
+++ b/app/src/main/java/com/alphawallet/app/viewmodel/NFTViewModel.java
@@ -96,15 +96,6 @@ public AssetDefinitionService getAssetDefinitionService()
return this.assetDefinitionService;
}
- public void showSendToken(Activity act, Wallet wallet, Token token)
- {
- if (token != null)
- {
- new SendTokenRouter().open(act, wallet.address, token.getSymbol(), token.tokenInfo.decimals,
- wallet, token, token.tokenInfo.chainId);
- }
- }
-
public Realm getRealmInstance(Wallet wallet)
{
return tokensService.getRealmInstance(wallet);
diff --git a/app/src/main/java/com/alphawallet/app/viewmodel/SendViewModel.java b/app/src/main/java/com/alphawallet/app/viewmodel/SendViewModel.java
index 9c91b3b9ca..4600906fc4 100644
--- a/app/src/main/java/com/alphawallet/app/viewmodel/SendViewModel.java
+++ b/app/src/main/java/com/alphawallet/app/viewmodel/SendViewModel.java
@@ -3,7 +3,9 @@
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.util.Pair;
+import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.alphawallet.app.C;
@@ -14,9 +16,13 @@
import com.alphawallet.app.entity.TransactionReturn;
import com.alphawallet.app.entity.Wallet;
import com.alphawallet.app.entity.tokens.Token;
+import com.alphawallet.app.entity.tokens.TokenCardMeta;
import com.alphawallet.app.entity.tokens.TokenInfo;
import com.alphawallet.app.interact.CreateTransactionInteract;
+import com.alphawallet.app.interact.FetchTokensInteract;
+import com.alphawallet.app.interact.GenericWalletInteract;
import com.alphawallet.app.repository.EthereumNetworkRepositoryType;
+import com.alphawallet.app.repository.PreferenceRepositoryType;
import com.alphawallet.app.repository.TokenRepository;
import com.alphawallet.app.router.MyAddressRouter;
import com.alphawallet.app.service.AnalyticsServiceType;
@@ -31,6 +37,10 @@
import java.math.BigDecimal;
import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
import javax.inject.Inject;
@@ -45,6 +55,8 @@ public class SendViewModel extends BaseViewModel implements TransactionSendHandl
private final MutableLiveData finalisedToken = new MutableLiveData<>();
private final MutableLiveData transactionFinalised = new MutableLiveData<>();
private final MutableLiveData transactionError = new MutableLiveData<>();
+ private final MutableLiveData wallet = new MutableLiveData<>();
+ private final MutableLiveData> tokens = new MutableLiveData<>();
private final MyAddressRouter myAddressRouter;
private final EthereumNetworkRepositoryType networkRepository;
@@ -53,6 +65,9 @@ public class SendViewModel extends BaseViewModel implements TransactionSendHandl
private final AssetDefinitionService assetDefinitionService;
private final KeyService keyService;
private final CreateTransactionInteract createTransactionInteract;
+ private final PreferenceRepositoryType preferenceRepository;
+ private final GenericWalletInteract genericWalletInteract;
+ private final FetchTokensInteract fetchTokensInteract;
@Inject
public SendViewModel(MyAddressRouter myAddressRouter,
@@ -62,7 +77,10 @@ public SendViewModel(MyAddressRouter myAddressRouter,
GasService gasService,
AssetDefinitionService assetDefinitionService,
KeyService keyService,
- AnalyticsServiceType analyticsService)
+ AnalyticsServiceType analyticsService,
+ PreferenceRepositoryType preferenceRepository,
+ GenericWalletInteract genericWalletInteract,
+ FetchTokensInteract fetchTokensInteract)
{
this.myAddressRouter = myAddressRouter;
this.networkRepository = ethereumNetworkRepositoryType;
@@ -71,9 +89,85 @@ public SendViewModel(MyAddressRouter myAddressRouter,
this.assetDefinitionService = assetDefinitionService;
this.keyService = keyService;
this.createTransactionInteract = createTransactionInteract;
+ this.preferenceRepository = preferenceRepository;
+ this.genericWalletInteract = genericWalletInteract;
+ this.fetchTokensInteract = fetchTokensInteract;
setAnalyticsService(analyticsService);
}
+ public void prepare()
+ {
+ disposable = genericWalletInteract.find()
+ .observeOn(Schedulers.io())
+ .subscribeOn(Schedulers.io())
+ .subscribe(wallet::postValue, this::onError);
+ }
+
+ public void fetchTokens()
+ {
+ disposable = fetchTokensInteract.fetchTokenMetas(wallet.getValue(), tokensService.getNetworkFilters(), assetDefinitionService)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(this::onTokenMetas, this::onError);
+ }
+
+ private void onTokenMetas(TokenCardMeta[] tokenCardMetas)
+ {
+ Arrays.sort(tokenCardMetas, Comparator.comparing(TokenCardMeta::getNameWeight));
+ List tokenList = new ArrayList<>();
+ int i = 0;
+ for (TokenCardMeta meta : tokenCardMetas)
+ {
+ Pair lastSentToken = getLastSentToken();
+ Token t = tokensService.getToken(meta.getChain(), meta.getAddress());
+ if (t.balance.compareTo(BigDecimal.ZERO) > 0 && !t.isNonFungible())
+ {
+ boolean isLastSentToken = t.tokenInfo.address.equalsIgnoreCase(lastSentToken.first);
+ if (i == 0 && isLastSentToken && t.tokenInfo.chainId == lastSentToken.second)
+ {
+ tokenList.add(i++, t); // Insert at the top
+ }
+ else if (i > 0 && isLastSentToken)
+ {
+ // Will only trigger if last sent token is a network token
+ // List other network tokens below last sent
+ tokenList.add(i++, t);
+ }
+ else
+ {
+ tokenList.add(t);
+ }
+ }
+ }
+
+ tokens.postValue(tokenList);
+ }
+
+ public Pair getLastSentToken()
+ {
+ return preferenceRepository.getLastSentToken();
+ }
+
+ public void setLastSentToken(Token token)
+ {
+ preferenceRepository.setLastSentToken(token);
+ }
+
+ public LiveData> tokens()
+ {
+ return tokens;
+ }
+
+ public MutableLiveData wallet()
+ {
+ return wallet;
+ }
+
+ public MutableLiveData finalisedToken()
+ {
+ return finalisedToken;
+ }
+
public MutableLiveData transactionFinalised()
{
return transactionFinalised;
@@ -94,6 +188,16 @@ public NetworkInfo getNetworkInfo(long chainId)
return networkRepository.getNetworkByChain(chainId);
}
+ public boolean isValidChain(long chainId)
+ {
+ return networkRepository.getNetworkByChain(chainId) != null;
+ }
+
+ public boolean isNetworkEnabled(long chainId)
+ {
+ return networkRepository.getFilterNetworkList().contains(chainId);
+ }
+
public Token getToken(long chainId, String tokenAddress)
{
return tokensService.getToken(chainId, tokenAddress);
@@ -110,17 +214,17 @@ public void showImportLink(Context context, String importTxt)
public void fetchToken(long chainId, String address, String walletAddress)
{
tokensService.update(address, chainId, ContractType.NOT_SET)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(tokenInfo -> gotTokenUpdate(tokenInfo, walletAddress), this::onError).isDisposed();
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(tokenInfo -> gotTokenUpdate(tokenInfo, walletAddress), this::onError).isDisposed();
}
private void gotTokenUpdate(TokenInfo tokenInfo, String walletAddress)
{
disposable = tokensService.addToken(tokenInfo, walletAddress)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(finalisedToken::postValue, this::onError);
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(finalisedToken::postValue, this::onError);
}
public AssetDefinitionService getAssetDefinitionService()
diff --git a/app/src/main/java/com/alphawallet/app/viewmodel/SwapViewModel.java b/app/src/main/java/com/alphawallet/app/viewmodel/SwapViewModel.java
index a826abf71e..f030e1ac88 100644
--- a/app/src/main/java/com/alphawallet/app/viewmodel/SwapViewModel.java
+++ b/app/src/main/java/com/alphawallet/app/viewmodel/SwapViewModel.java
@@ -17,7 +17,7 @@
import com.alphawallet.app.entity.lifi.Connection;
import com.alphawallet.app.entity.lifi.Quote;
import com.alphawallet.app.entity.lifi.SwapProvider;
-import com.alphawallet.app.entity.lifi.Token;
+import com.alphawallet.app.entity.lifi.LifiToken;
import com.alphawallet.app.interact.CreateTransactionInteract;
import com.alphawallet.app.repository.PreferenceRepositoryType;
import com.alphawallet.app.repository.SwapRepositoryType;
@@ -197,7 +197,7 @@ public void getConnections(long from, long to)
.subscribe(this::onConnections, this::onConnectionsError);
}
- public void getQuote(Token source, Token dest, String address, String amount, String slippage, String allowExchanges)
+ public void getQuote(LifiToken source, LifiToken dest, String address, String amount, String slippage, String allowExchanges)
{
if (!isValidAmount(amount)) return;
if (hasEnoughBalance(source, amount))
@@ -243,7 +243,7 @@ private void onQuoteError(Throwable t)
postError(C.ErrorCode.SWAP_QUOTE_ERROR, Objects.requireNonNull(t.getMessage()));
}
- public boolean hasEnoughBalance(Token source, String amount)
+ public boolean hasEnoughBalance(LifiToken source, String amount)
{
BigDecimal bal = new BigDecimal(getBalance(source));
BigDecimal reqAmount = new BigDecimal(amount);
@@ -354,7 +354,7 @@ private boolean isValidQuote(String result)
&& result.contains("tool");
}
- public String getBalance(Token token)
+ public String getBalance(LifiToken token)
{
com.alphawallet.app.entity.tokens.Token t;
if (token.isNativeToken())
@@ -442,8 +442,8 @@ protected void onCleared()
public void getRoutes(Activity activity,
ActivityResultLauncher launcher,
- Token source,
- Token dest,
+ LifiToken source,
+ LifiToken dest,
String address,
String amount,
String slippage)
diff --git a/app/src/main/java/com/alphawallet/app/widget/AWalletBottomNavigationView.java b/app/src/main/java/com/alphawallet/app/widget/AWalletBottomNavigationView.java
index 3f2d07a95e..2eccf3e6bb 100644
--- a/app/src/main/java/com/alphawallet/app/widget/AWalletBottomNavigationView.java
+++ b/app/src/main/java/com/alphawallet/app/widget/AWalletBottomNavigationView.java
@@ -6,7 +6,6 @@
import static com.alphawallet.app.entity.WalletPage.WALLET;
import android.content.Context;
-import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
@@ -14,7 +13,6 @@
import android.widget.TextView;
import androidx.annotation.Nullable;
-import androidx.core.content.res.ResourcesCompat;
import com.alphawallet.app.R;
import com.alphawallet.app.entity.WalletPage;
@@ -27,10 +25,10 @@ public class AWalletBottomNavigationView extends LinearLayout
private final TextView walletLabel;
private final TextView settingsBadge;
private final TextView settingsLabel;
+
+ private final TextView sendButton;
private final RelativeLayout settingsTab;
private final TextView activityLabel;
- private final Typeface regularTypeface;
- private final Typeface semiboldTypeface;
private final ArrayList settingsBadgeKeys = new ArrayList<>();
private OnBottomNavigationItemSelectedListener listener;
private WalletPage selectedItem;
@@ -41,6 +39,7 @@ public AWalletBottomNavigationView(Context context, @Nullable AttributeSet attrs
inflate(context, R.layout.layout_bottom_navigation, this);
walletLabel = findViewById(R.id.nav_wallet_text);
activityLabel = findViewById(R.id.nav_activity_text);
+ sendButton = findViewById(R.id.nav_send);
dappBrowserLabel = findViewById(R.id.nav_browser_text);
settingsTab = findViewById(R.id.settings_tab);
settingsLabel = findViewById(R.id.nav_settings_text);
@@ -51,13 +50,15 @@ public AWalletBottomNavigationView(Context context, @Nullable AttributeSet attrs
dappBrowserLabel.setOnClickListener(v -> selectItem(DAPP_BROWSER));
settingsTab.setOnClickListener(v -> selectItem(SETTINGS));
- regularTypeface = ResourcesCompat.getFont(getContext(), R.font.font_regular);
- semiboldTypeface = ResourcesCompat.getFont(getContext(), R.font.font_semibold);
-
// set wallet fragment selected on start
setSelectedItem(WALLET);
}
+ public void setSendButtonListener(View.OnClickListener listener)
+ {
+ sendButton.setOnClickListener(listener);
+ }
+
public void setListener(OnBottomNavigationItemSelectedListener listener)
{
this.listener = listener;
@@ -81,19 +82,15 @@ public void setSelectedItem(WalletPage index)
{
case DAPP_BROWSER:
dappBrowserLabel.setSelected(true);
- dappBrowserLabel.setTypeface(semiboldTypeface);
break;
case WALLET:
walletLabel.setSelected(true);
- walletLabel.setTypeface(semiboldTypeface);
break;
case SETTINGS:
settingsLabel.setSelected(true);
- settingsLabel.setTypeface(semiboldTypeface);
break;
case ACTIVITY:
activityLabel.setSelected(true);
- activityLabel.setTypeface(semiboldTypeface);
break;
}
}
@@ -101,13 +98,9 @@ public void setSelectedItem(WalletPage index)
private void deselectAll()
{
dappBrowserLabel.setSelected(false);
- dappBrowserLabel.setTypeface(regularTypeface);
walletLabel.setSelected(false);
- walletLabel.setTypeface(regularTypeface);
settingsLabel.setSelected(false);
- settingsLabel.setTypeface(regularTypeface);
activityLabel.setSelected(false);
- activityLabel.setTypeface(regularTypeface);
}
public void setSettingsBadgeCount(int count)
diff --git a/app/src/main/java/com/alphawallet/app/widget/ChainName.java b/app/src/main/java/com/alphawallet/app/widget/ChainName.java
index c7038ca512..83ff8453f1 100644
--- a/app/src/main/java/com/alphawallet/app/widget/ChainName.java
+++ b/app/src/main/java/com/alphawallet/app/widget/ChainName.java
@@ -2,9 +2,7 @@
import android.content.Context;
import android.content.res.TypedArray;
-import android.text.TextUtils;
import android.util.AttributeSet;
-import android.util.TypedValue;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -20,6 +18,7 @@
*/
public class ChainName extends LinearLayout
{
+ private final LinearLayout layout;
private final TextView chainName;
private boolean invertNameColour;
@@ -27,6 +26,7 @@ public ChainName(Context context, @Nullable AttributeSet attrs)
{
super(context, attrs);
inflate(context, R.layout.item_chain_name, this);
+ layout = findViewById(R.id.layout_chain_name);
chainName = findViewById(R.id._text_chain_name);
getAttrs(context, attrs);
}
@@ -41,12 +41,12 @@ public void setChainID(long chainId)
if (invertNameColour)
{
chainName.setTextColor(getContext().getColor(EthereumNetworkBase.getChainColour(chainId)));
- chainName.setBackgroundResource(R.drawable.background_chain_inverse);
+ layout.setBackgroundResource(R.drawable.background_chain_inverse);
}
else
{
chainName.setTextColor(getContext().getColor(R.color.white));
- chainName.getBackground().setTint(ContextCompat.getColor(getContext(),
+ layout.getBackground().setTint(ContextCompat.getColor(getContext(),
EthereumNetworkBase.getChainColour(chainId)));
}
}
@@ -59,15 +59,15 @@ public void setChainID(long chainId)
private void getAttrs(Context context, AttributeSet attrs)
{
TypedArray a = context.getTheme().obtainStyledAttributes(
- attrs,
- R.styleable.InputView,
- 0, 0
+ attrs,
+ R.styleable.InputView,
+ 0, 0
);
try
{
- int fontSize = a.getInteger(R.styleable.InputView_font_size, 12);
- chainName.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize);
+// int fontSize = a.getInteger(R.styleable.InputView_font_size, 12);
+// chainName.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize);
invertNameColour = a.getBoolean(R.styleable.InputView_invert, false);
}
finally
diff --git a/app/src/main/java/com/alphawallet/app/widget/InputAddress.java b/app/src/main/java/com/alphawallet/app/widget/InputAddress.java
index f21de52ad8..898742e876 100644
--- a/app/src/main/java/com/alphawallet/app/widget/InputAddress.java
+++ b/app/src/main/java/com/alphawallet/app/widget/InputAddress.java
@@ -328,7 +328,11 @@ public void ENSResolved(String address, String ens)
{
errorText.setVisibility(View.GONE);
setWaitingSpinner(false);
- bindAvatar(address, ens);
+ if (!TextUtils.isEmpty(address) && !TextUtils.isEmpty(ens))
+ {
+ bindAvatar(address, ens);
+ }
+
if (addressReadyCallback != null)
{
addressReadyCallback.resolvedAddress(address, ens);
@@ -552,6 +556,16 @@ public long getChain()
return chainOverride;
}
+ public void showControls(boolean show)
+ {
+ pasteItem.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+
+ public void setEditable(boolean editable)
+ {
+ editText.setEnabled(editable);
+ }
+
/*public void setEnsNodeNotSyncCallback(EnsNodeNotSyncCallback callback)
{
Timber.d("setEnsNodeNotSyncCallback: ");
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..eacc461594 100644
--- a/app/src/main/java/com/alphawallet/app/widget/InputAmount.java
+++ b/app/src/main/java/com/alphawallet/app/widget/InputAmount.java
@@ -1,15 +1,21 @@
package com.alphawallet.app.widget;
+import static com.alphawallet.app.C.GAS_LIMIT_MIN;
+import static com.alphawallet.app.repository.TokensRealmSource.databaseKey;
+
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Handler;
import android.os.Looper;
import android.text.Editable;
+import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.View;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
+import android.widget.RelativeLayout;
import android.widget.TextView;
import com.alphawallet.app.R;
@@ -26,6 +32,7 @@
import com.alphawallet.app.ui.widget.entity.AmountReadyCallback;
import com.alphawallet.app.ui.widget.entity.NumericInput;
import com.alphawallet.app.util.BalanceUtils;
+import com.google.android.material.button.MaterialButton;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -41,9 +48,6 @@
import io.realm.RealmQuery;
import timber.log.Timber;
-import static com.alphawallet.app.C.GAS_LIMIT_MIN;
-import static com.alphawallet.app.repository.TokensRealmSource.databaseKey;
-
/**
* Created by JB on 10/11/2020.
*/
@@ -54,9 +58,15 @@ public class InputAmount extends LinearLayout
private final TextView symbolText;
private final TokenIcon icon;
private final StandardHeader header;
- private final TextView availableSymbol;
+ private final RelativeLayout headerLayout;
+ private final ChainName chainName;
private final TextView availableAmount;
+ private final TextView equivalent;
private final TextView allFunds;
+ private final ImageView switchButton;
+ private final ImageView caret;
+ private final LinearLayout clickMore;
+ private final MaterialButton selectTokenButton;
private final ProgressBar gasFetch;
private Token token;
private Realm realm;
@@ -68,12 +78,12 @@ public class InputAmount extends LinearLayout
private final Handler handler = new Handler(Looper.getMainLooper());
private AmountReadyCallback amountReadyCallback;
private boolean amountReady;
-
+ private boolean showingCrypto;
+ private boolean isEditable = true;
//These need to be members because the listener is shut down if the object doesn't exist
private RealmTokenTicker realmTickerUpdate;
private RealmToken realmTokenUpdate;
- private boolean showingCrypto;
public InputAmount(Context context, AttributeSet attrs)
{
@@ -85,16 +95,50 @@ public InputAmount(Context context, AttributeSet attrs)
symbolText = findViewById(R.id.text_token_symbol);
icon = findViewById(R.id.token_icon);
header = findViewById(R.id.header);
- availableSymbol = findViewById(R.id.text_symbol);
+ headerLayout = findViewById(R.id.layout_header);
+ chainName = findViewById(R.id.chain);
availableAmount = findViewById(R.id.text_available);
allFunds = findViewById(R.id.text_all_funds);
gasFetch = findViewById(R.id.gas_fetch_progress);
+ clickMore = findViewById(R.id.layout_more_click);
+ selectTokenButton = findViewById(R.id.btn_select_token);
+ switchButton = findViewById(R.id.btn_switch);
+ caret = findViewById(R.id.expand_more);
+ equivalent = findViewById(R.id.equivalent);
showingCrypto = !CustomViewSettings.inputAmountFiatDefault();
amountReady = false;
setupAttrs(context, attrs);
+ }
- setupViewListeners();
+ private void setupAttrs(Context context, AttributeSet attrs)
+ {
+ TypedArray a = context.getTheme().obtainStyledAttributes(
+ attrs,
+ R.styleable.InputView,
+ 0, 0
+ );
+
+ try
+ {
+ boolean showHeader = a.getBoolean(R.styleable.InputView_show_header, true);
+ boolean showAllFunds = a.getBoolean(R.styleable.InputView_show_allFunds, true);
+ boolean showChainName = a.getBoolean(R.styleable.InputView_showChainName, true);
+ boolean currencyMode = a.getBoolean(R.styleable.InputView_currencyMode, false);
+ int headerTextId = a.getResourceId(R.styleable.InputView_label, R.string.amount);
+ headerLayout.setVisibility(showHeader ? View.VISIBLE : View.GONE);
+ allFunds.setVisibility(showAllFunds ? View.VISIBLE : View.GONE);
+ header.setText(headerTextId);
+ if (currencyMode)
+ {
+ symbolText.setText(TickerService.getCurrencySymbolTxt());
+ icon.showLocalCurrency();
+ }
+ }
+ finally
+ {
+ a.recycle();
+ }
}
/**
@@ -105,21 +149,34 @@ public InputAmount(Context context, AttributeSet attrs)
* @param assetDefinitionService
* @param svs
*/
- public void setupToken(@NotNull Token token, @Nullable AssetDefinitionService assetDefinitionService,
- @NotNull TokensService svs, @NotNull AmountReadyCallback amountCallback)
+ public void setupToken(@NotNull Token token,
+ @Nullable AssetDefinitionService assetDefinitionService,
+ @NotNull TokensService svs,
+ @NotNull AmountReadyCallback amountCallback)
{
this.token = token;
this.tokensService = svs;
this.assetService = assetDefinitionService;
this.amountReadyCallback = amountCallback;
- icon.bindData(token, assetService);
- header.getChainName().setChainID(token.tokenInfo.chainId);
- updateAvailableBalance();
-
this.realm = tokensService.getWalletRealmInstance();
this.tickerRealm = tokensService.getTickerRealmInstance();
+
+ selectTokenButton.setVisibility(View.GONE);
+ clickMore.setVisibility(View.VISIBLE);
+ chainName.setVisibility(View.VISIBLE);
+ chainName.setChainID(token.tokenInfo.chainId);
+
+ icon.bindData(token, assetService);
+
bindDataSource();
+
setupAllFunds();
+
+ setupViewListeners();
+
+ updateAvailableBalance();
+
+ updateEquivalent();
}
public void getInputAmount()
@@ -215,14 +272,14 @@ private void bindDataSource()
if (realmTokenUpdate != null) realmTokenUpdate.removeAllChangeListeners();
realmTokenUpdate = realm.where(RealmToken.class)
- .equalTo("address", databaseKey(token.tokenInfo.chainId, token.tokenInfo.address.toLowerCase()), Case.INSENSITIVE)
- .findFirstAsync();
+ .equalTo("address", databaseKey(token.tokenInfo.chainId, token.tokenInfo.address.toLowerCase()), Case.INSENSITIVE)
+ .findFirstAsync();
//if the token doesn't exist yet, first ask the TokensService to pick it up
tokensService.storeToken(token);
realmTokenUpdate.addChangeListener(realmToken -> {
- RealmToken rt = (RealmToken)realmToken;
+ RealmToken rt = (RealmToken) realmToken;
if (rt.isValid() && exactAmount.compareTo(BigDecimal.ZERO) == 0)
{
token = tokensService.getToken(rt.getChainId(), rt.getTokenAddress());
@@ -231,37 +288,59 @@ private void bindDataSource()
});
}
- private void setupViewListeners()
+ public void setListener(OnClickListener listener)
{
- LinearLayout clickMore = findViewById(R.id.layout_more_click);
-
- clickMore.setOnClickListener(v -> {
- //on down caret clicked - switch to fiat currency equivalent if there's a ticker
- if (getTickerQuery() == null) return;
+ if (listener != null)
+ {
+ caret.setVisibility(View.VISIBLE);
+ clickMore.setOnClickListener(listener);
+ selectTokenButton.setOnClickListener(listener);
+ }
+ else
+ {
+ caret.setVisibility(View.GONE);
+ }
+ }
- RealmTokenTicker rtt = getTickerQuery().findFirst();
- if (showingCrypto && rtt != null)
- {
- showingCrypto = false;
- startTickerListener();
- }
- else
- {
- showingCrypto = true;
- if (tickerRealm != null) tickerRealm.removeAllChangeListeners(); //stop ticker listener
- }
+ private void setupViewListeners()
+ {
+ if (getTickerQuery() != null && isEditable)
+ {
+ switchButton.setVisibility(View.VISIBLE);
+ switchButton.setOnClickListener(v -> {
+ RealmTokenTicker rtt = getTickerQuery().findFirst();
+ if (showingCrypto && rtt != null)
+ {
+ showingCrypto = false;
+ startTickerListener();
+ }
+ else
+ {
+ showingCrypto = true;
+ if (tickerRealm != null)
+ tickerRealm.removeAllChangeListeners(); //stop ticker listener
+ }
- updateAvailableBalance();
- });
+ updateAvailableBalance();
+ updateEquivalent();
+ });
+ }
+ else
+ {
+ switchButton.setVisibility(View.GONE);
+ }
- editText.addTextChangedListener(new TextWatcher() {
+ editText.addTextChangedListener(new TextWatcher()
+ {
@Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ public void beforeTextChanged(CharSequence s, int start, int count, int after)
+ {
}
@Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
+ public void onTextChanged(CharSequence s, int start, int before, int count)
+ {
if (editText.hasFocus())
{
exactAmount = BigDecimal.ZERO; //invalidate the 'all funds' amount
@@ -270,7 +349,9 @@ public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
- public void afterTextChanged(Editable s) {
+ public void afterTextChanged(Editable s)
+ {
+ updateEquivalent();
if (editText.hasFocus())
{
amountReadyCallback.updateCryptoAmount(getWeiInputAmount());
@@ -295,7 +376,7 @@ private RealmQuery getTickerQuery()
if (tickerRealm != null)
{
return tickerRealm.where(RealmTokenTicker.class)
- .equalTo("contract", TokensRealmSource.databaseKey(token.tokenInfo.chainId, token.isEthereum() ? "eth" : token.getAddress().toLowerCase()));
+ .equalTo("contract", TokensRealmSource.databaseKey(token.tokenInfo.chainId, token.isEthereum() ? "eth" : token.getAddress().toLowerCase()));
}
else
{
@@ -317,11 +398,48 @@ private void showCrypto()
{
icon.bindData(token, assetService);
symbolText.setText(token.getSymbol());
- availableSymbol.setText(token.getSymbol());
- availableAmount.setText(token.getStringBalanceForUI(5));
+
+ availableAmount.setText(String.format("%s: %s %s", context.getString(R.string.balance), token.getStringBalanceForUI(5), token.getSymbol()));
+
updateAllFundsAmount();
}
+ private void updateEquivalent()
+ {
+ if (getTickerQuery() == null)
+ {
+ equivalent.setVisibility(View.GONE);
+ switchButton.setVisibility(View.GONE);
+ return;
+ }
+
+ RealmTokenTicker rtt = getTickerQuery().findFirst();
+ if (rtt == null)
+ {
+ equivalent.setVisibility(View.GONE);
+ switchButton.setVisibility(View.GONE);
+ return;
+ }
+
+ double cryptoRate = Double.parseDouble(rtt.getPrice());
+ BigDecimal amount = editText.getBigDecimalValue();
+ BigDecimal rate = new BigDecimal(cryptoRate);
+
+ BigDecimal value;
+ if (showingCrypto)
+ {
+ value = amount.multiply(rate).setScale(2, RoundingMode.CEILING);
+ equivalent.setText(String.format("%s %s", rtt.getCurrencySymbol(), value));
+ }
+ else
+ {
+ value = amount.divide(rate, 4, RoundingMode.FLOOR);
+ equivalent.setText(String.format("%s %s", value, token.tokenInfo.symbol));
+ }
+
+ equivalent.setVisibility(View.VISIBLE);
+ }
+
private void showFiat()
{
icon.showLocalCurrency();
@@ -335,18 +453,18 @@ private void showFiat()
if (rtt != null)
{
- String currencyLabel = rtt.getCurrencySymbol() + TickerService.getCurrencySymbol();
+ String currencyLabel = rtt.getCurrencySymbol();
symbolText.setText(currencyLabel);
//calculate available fiat
double cryptoRate = Double.parseDouble(rtt.getPrice());
double availableCryptoBalance = token.getCorrectedBalance(18).doubleValue();
- availableAmount.setText(TickerService.getCurrencyString(availableCryptoBalance * cryptoRate));
- availableSymbol.setText(rtt.getCurrencySymbol());
+ BigDecimal balance = new BigDecimal(cryptoRate).multiply(new BigDecimal(availableCryptoBalance)).setScale(2, RoundingMode.FLOOR);
+
+ availableAmount.setText(String.format("%s: %s %s", context.getString(R.string.balance), balance.toString(), rtt.getCurrencySymbol()));
+
updateAllFundsAmount(); //update amount if showing 'All Funds'
- amountReadyCallback.updateCryptoAmount(
- getWeiInputAmount()
- ); //now update
+ amountReadyCallback.updateCryptoAmount(getWeiInputAmount()); //now update
}
updateAllFundsAmount();
}
@@ -384,8 +502,8 @@ private void setupAllFunds()
if (token.isEthereum() && token.hasPositiveBalance())
{
RealmGasSpread gasSpread = tokensService.getTickerRealmInstance().where(RealmGasSpread.class)
- .equalTo("chainId", token.tokenInfo.chainId)
- .findFirst();
+ .equalTo("chainId", token.tokenInfo.chainId)
+ .findFirst();
if (gasSpread != null && gasSpread.getGasPrice().compareTo(BigInteger.ZERO) > 0)
{
@@ -397,8 +515,8 @@ private void setupAllFunds()
gasFetch.setVisibility(View.VISIBLE);
Web3j web3j = TokenRepository.getWeb3jService(token.tokenInfo.chainId);
web3j.ethGasPrice().sendAsync()
- .thenAccept(ethGasPrice -> onLatestGasPrice(ethGasPrice.getGasPrice()))
- .exceptionally(this::onGasFetchError);
+ .thenAccept(ethGasPrice -> onLatestGasPrice(ethGasPrice.getGasPrice()))
+ .exceptionally(this::onGasFetchError);
}
}
else
@@ -446,37 +564,6 @@ public void run()
}
};
- private void setupAttrs(Context context, AttributeSet attrs)
- {
- TypedArray a = context.getTheme().obtainStyledAttributes(
- attrs,
- R.styleable.InputView,
- 0, 0
- );
-
- try
- {
- boolean showHeader = a.getBoolean(R.styleable.InputView_show_header, true);
- boolean showAllFunds = a.getBoolean(R.styleable.InputView_show_allFunds, true);
- boolean showChainName = a.getBoolean(R.styleable.InputView_showChainName, true);
- boolean currencyMode = a.getBoolean(R.styleable.InputView_currencyMode, false);
- int headerTextId = a.getResourceId(R.styleable.InputView_label, R.string.amount);
- header.setVisibility(showHeader ? View.VISIBLE : View.GONE);
- allFunds.setVisibility(showAllFunds ? View.VISIBLE : View.GONE);
- header.setText(headerTextId);
- header.getChainName().setVisibility(showChainName ? View.VISIBLE : View.GONE);
- if (currencyMode)
- {
- symbolText.setText(TickerService.getCurrencySymbolTxt());
- icon.showLocalCurrency();
- }
- }
- finally
- {
- a.recycle();
- }
- }
-
private Void onGasFetchError(Throwable throwable)
{
gasFetch.setVisibility(View.GONE);
@@ -553,4 +640,21 @@ public boolean isSendAll()
{
return exactAmount.compareTo(BigDecimal.ZERO) > 0;
}
+
+ public void focus()
+ {
+ editText.requestFocus();
+ }
+
+ public void showControls(boolean show)
+ {
+ switchButton.setVisibility(show ? View.VISIBLE : View.GONE);
+ allFunds.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+
+ public void setEditable(boolean editable)
+ {
+ this.isEditable = editable;
+ editText.setEnabled(editable);
+ }
}
diff --git a/app/src/main/java/com/alphawallet/app/widget/SelectLifiTokenDialog.java b/app/src/main/java/com/alphawallet/app/widget/SelectLifiTokenDialog.java
new file mode 100644
index 0000000000..80c81fca7b
--- /dev/null
+++ b/app/src/main/java/com/alphawallet/app/widget/SelectLifiTokenDialog.java
@@ -0,0 +1,107 @@
+package com.alphawallet.app.widget;
+
+import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.alphawallet.app.R;
+import com.alphawallet.app.entity.lifi.LifiToken;
+import com.alphawallet.app.ui.widget.adapter.SelectLifiTokenAdapter;
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
+import com.google.android.material.bottomsheet.BottomSheetDialog;
+
+import java.util.List;
+
+public class SelectLifiTokenDialog extends BottomSheetDialog
+{
+ private final Handler handler = new Handler(Looper.getMainLooper());
+ private RecyclerView tokenList;
+ private SelectLifiTokenAdapter adapter;
+ private LinearLayout searchLayout;
+ private EditText search;
+ private TextView noResultsText;
+
+ public SelectLifiTokenDialog(@NonNull Activity activity)
+ {
+ super(activity);
+ View view = View.inflate(getContext(), R.layout.dialog_select_token, null);
+ setContentView(view);
+
+ setOnShowListener(dialogInterface -> {
+ view.setMinimumHeight(Resources.getSystem().getDisplayMetrics().heightPixels);
+ BottomSheetBehavior behavior = BottomSheetBehavior.from((View) view.getParent());
+ behavior.setState(STATE_EXPANDED);
+ behavior.setSkipCollapsed(true);
+ });
+
+ tokenList = view.findViewById(R.id.token_list);
+ search = view.findViewById(R.id.edit_search);
+ searchLayout = view.findViewById(R.id.layout_search_tokens);
+ noResultsText = view.findViewById(R.id.no_results);
+ ImageView btnClose = view.findViewById(R.id.image_close);
+ btnClose.setOnClickListener(v -> dismiss());
+ }
+
+ public SelectLifiTokenDialog(List tokenItems, Activity activity, EventListener callback)
+ {
+ this(activity);
+
+ noResultsText.setVisibility(tokenItems.size() > 0 ? View.GONE : View.VISIBLE);
+
+ adapter = new SelectLifiTokenAdapter(tokenItems, callback);
+
+ tokenList.setLayoutManager(new LinearLayoutManager(getContext()));
+ tokenList.setAdapter(adapter);
+
+ searchLayout.setOnClickListener(v -> {
+ });
+
+ search.addTextChangedListener(new TextWatcher()
+ {
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count)
+ {
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after)
+ {
+ }
+
+ @Override
+ public void afterTextChanged(final Editable searchFilter)
+ {
+ handler.postDelayed(() -> {
+ if (adapter != null)
+ {
+ adapter.filter(searchFilter.toString());
+ }
+ }, 200);
+ }
+ });
+ }
+
+ public void setSelectedToken(String address)
+ {
+ adapter.setSelectedToken(address);
+ }
+
+ public interface EventListener
+ {
+ void onChainSelected(LifiToken tokenItem);
+ }
+}
diff --git a/app/src/main/java/com/alphawallet/app/widget/SelectTokenDialog.java b/app/src/main/java/com/alphawallet/app/widget/SelectTokenDialog.java
index bc2ce27ba8..80ba5a6147 100644
--- a/app/src/main/java/com/alphawallet/app/widget/SelectTokenDialog.java
+++ b/app/src/main/java/com/alphawallet/app/widget/SelectTokenDialog.java
@@ -19,7 +19,7 @@
import androidx.recyclerview.widget.RecyclerView;
import com.alphawallet.app.R;
-import com.alphawallet.app.entity.lifi.Token;
+import com.alphawallet.app.entity.tokens.Token;
import com.alphawallet.app.ui.widget.adapter.SelectTokenAdapter;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.bottomsheet.BottomSheetDialog;
@@ -54,15 +54,16 @@ public SelectTokenDialog(@NonNull Activity activity)
noResultsText = view.findViewById(R.id.no_results);
ImageView btnClose = view.findViewById(R.id.image_close);
btnClose.setOnClickListener(v -> dismiss());
+ search.requestFocus();
}
- public SelectTokenDialog(List tokenItems, Activity activity, SelectTokenDialogEventListener callback)
+ public SelectTokenDialog(List tokenItems, Activity activity, OnTokenClickListener listener)
{
this(activity);
noResultsText.setVisibility(tokenItems.size() > 0 ? View.GONE : View.VISIBLE);
- adapter = new SelectTokenAdapter(tokenItems, callback);
+ adapter = new SelectTokenAdapter(tokenItems, listener);
tokenList.setLayoutManager(new LinearLayoutManager(getContext()));
tokenList.setAdapter(adapter);
@@ -95,13 +96,8 @@ public void afterTextChanged(final Editable searchFilter)
});
}
- public void setSelectedToken(String address)
+ public interface OnTokenClickListener
{
- adapter.setSelectedToken(address);
- }
-
- public interface SelectTokenDialogEventListener
- {
- void onChainSelected(Token tokenItem);
+ void onTokenClicked(Token token);
}
}
diff --git a/app/src/main/java/com/alphawallet/app/widget/StandardHeader.java b/app/src/main/java/com/alphawallet/app/widget/StandardHeader.java
index 2cd0a27315..164de1a6a4 100644
--- a/app/src/main/java/com/alphawallet/app/widget/StandardHeader.java
+++ b/app/src/main/java/com/alphawallet/app/widget/StandardHeader.java
@@ -63,22 +63,30 @@ private void getAttrs(Context context, AttributeSet attrs)
if (showTextControl)
{
textControl.setVisibility(View.VISIBLE);
- textControl.setText(controlText);
}
else
{
textControl.setVisibility(View.GONE);
}
+ if (controlText != -1)
+ {
+ textControl.setText(controlText);
+ }
+
if (showImageControl)
{
imageControl.setVisibility(View.VISIBLE);
- imageControl.setImageResource(controlImageRes);
}
else
{
imageControl.setVisibility(View.GONE);
}
+
+ if (controlImageRes != -1)
+ {
+ imageControl.setImageResource(controlImageRes);
+ }
}
finally
{
diff --git a/app/src/main/java/com/alphawallet/app/widget/TokenSelector.java b/app/src/main/java/com/alphawallet/app/widget/TokenSelector.java
index 4c74fb4ec2..6b4a061228 100644
--- a/app/src/main/java/com/alphawallet/app/widget/TokenSelector.java
+++ b/app/src/main/java/com/alphawallet/app/widget/TokenSelector.java
@@ -16,7 +16,7 @@
import androidx.core.content.ContextCompat;
import com.alphawallet.app.R;
-import com.alphawallet.app.entity.lifi.Token;
+import com.alphawallet.app.entity.lifi.LifiToken;
import com.alphawallet.app.util.Utils;
import com.google.android.material.button.MaterialButton;
@@ -35,7 +35,7 @@ public class TokenSelector extends LinearLayout
private final TextView error;
private Runnable runnable;
private TokenSelectorEventListener callback;
- private Token tokenItem;
+ private LifiToken tokenItem;
public TokenSelector(Context context, AttributeSet attrs)
{
@@ -143,7 +143,7 @@ public void reset()
setVisibility(View.VISIBLE);
}
- public void init(Token tokenItem)
+ public void init(LifiToken tokenItem)
{
this.tokenItem = tokenItem;
@@ -189,7 +189,7 @@ public void afterTextChanged(Editable editable)
});
}
- public Token getToken()
+ public LifiToken getToken()
{
return this.tokenItem;
}
@@ -255,7 +255,7 @@ public interface TokenSelectorEventListener
/**
* Triggered when a new Token is selected.
**/
- void onSelectionChanged(Token token);
+ void onSelectionChanged(LifiToken token);
/**
* Triggered when the `Max` button is clicked.
diff --git a/app/src/main/res/drawable/background_chain_inverse.xml b/app/src/main/res/drawable/background_chain_inverse.xml
index 74e5f5728c..8c0f89c2d2 100644
--- a/app/src/main/res/drawable/background_chain_inverse.xml
+++ b/app/src/main/res/drawable/background_chain_inverse.xml
@@ -2,5 +2,5 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/background_chain_name.xml b/app/src/main/res/drawable/background_chain_name.xml
index 926e786462..f78e8d3ac0 100644
--- a/app/src/main/res/drawable/background_chain_name.xml
+++ b/app/src/main/res/drawable/background_chain_name.xml
@@ -2,5 +2,5 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_arrow_send.xml b/app/src/main/res/drawable/ic_arrow_send.xml
new file mode 100644
index 0000000000..b9cfad770c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_arrow_send.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml
index db9b164d78..7421fe3bc8 100644
--- a/app/src/main/res/layout/activity_home.xml
+++ b/app/src/main/res/layout/activity_home.xml
@@ -18,7 +18,7 @@
diff --git a/app/src/main/res/layout/activity_send.xml b/app/src/main/res/layout/activity_send.xml
index 594d82b07f..66cbd703fb 100644
--- a/app/src/main/res/layout/activity_send.xml
+++ b/app/src/main/res/layout/activity_send.xml
@@ -26,18 +26,6 @@
android:layout_height="wrap_content"
android:orientation="vertical">
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_sign_method.xml b/app/src/main/res/layout/dialog_sign_method.xml
index 6cfafcf2a6..2aec670ec2 100644
--- a/app/src/main/res/layout/dialog_sign_method.xml
+++ b/app/src/main/res/layout/dialog_sign_method.xml
@@ -145,7 +145,6 @@
style="@style/Aw.Typography.Label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginBottom="2dp"
android:text="@string/ethereum" />
diff --git a/app/src/main/res/layout/item_chain_name.xml b/app/src/main/res/layout/item_chain_name.xml
index ad0545b789..7d0ccf145a 100644
--- a/app/src/main/res/layout/item_chain_name.xml
+++ b/app/src/main/res/layout/item_chain_name.xml
@@ -1,21 +1,24 @@
+ android:id="@+id/layout_chain_name"
+ android:background="@drawable/background_chain_name"
+ android:orientation="horizontal"
+ android:gravity="center"
+ android:paddingStart="@dimen/standard_16"
+ android:paddingEnd="@dimen/standard_16"
+ android:paddingVertical="@dimen/mini_4">
4dp
60dp
+ 60dp
12dp
60dp
5dp
+ 100dp
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 1786451d8f..9a20891bd1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -410,7 +410,7 @@
%1$s Blockchain
0.00 %s
Telegram (Customer Support)
- Transfer request
+ Transfer Request
Token transfer request: %1$s
Generate Payment Request
Enter the requested amount:
@@ -1055,4 +1055,5 @@
Get notified
You will not receive notifications while AlphaWallet is in the background. You can change this by going to Settings > Notifications
Minted
+ Payment Request
diff --git a/app/src/main/res/values/typography.xml b/app/src/main/res/values/typography.xml
index fc9127f41e..ed7ac620eb 100644
--- a/app/src/main/res/values/typography.xml
+++ b/app/src/main/res/values/typography.xml
@@ -90,6 +90,10 @@
- @dimen/sp12
+
+
diff --git a/app/src/test/java/com/alphawallet/app/entity/TokensMappingTest.java b/app/src/test/java/com/alphawallet/app/entity/LifiTokenUtilsMappingTest.java
similarity index 93%
rename from app/src/test/java/com/alphawallet/app/entity/TokensMappingTest.java
rename to app/src/test/java/com/alphawallet/app/entity/LifiTokenUtilsMappingTest.java
index df579b54ce..baad86c554 100644
--- a/app/src/test/java/com/alphawallet/app/entity/TokensMappingTest.java
+++ b/app/src/test/java/com/alphawallet/app/entity/LifiTokenUtilsMappingTest.java
@@ -15,11 +15,12 @@
import java.util.Collection;
@RunWith(Parameterized.class)
-public class TokensMappingTest {
+public class LifiTokenUtilsMappingTest
+{
private String groupString;
private TokenGroup groupEnum;
- public TokensMappingTest(String groupString, TokenGroup groupEnum) {
+ public LifiTokenUtilsMappingTest(String groupString, TokenGroup groupEnum) {
this.groupString = groupString;
this.groupEnum = groupEnum;
}
diff --git a/app/src/test/java/com/alphawallet/app/entity/lifi/ActionTest.java b/app/src/test/java/com/alphawallet/app/entity/lifi/ActionTest.java
index eed2ee523c..bfab1868a4 100644
--- a/app/src/test/java/com/alphawallet/app/entity/lifi/ActionTest.java
+++ b/app/src/test/java/com/alphawallet/app/entity/lifi/ActionTest.java
@@ -11,8 +11,8 @@ public class ActionTest
public void should_return_current_price()
{
Action action = new Action();
- action.fromToken = new Token();
- action.toToken = new Token();
+ action.fromToken = new LifiToken();
+ action.toToken = new LifiToken();
action.fromToken.priceUSD = "5";
action.fromToken.decimals = 18;
action.toToken.priceUSD = "1000";
diff --git a/app/src/test/java/com/alphawallet/app/entity/lifi/TokenTest.java b/app/src/test/java/com/alphawallet/app/entity/lifi/LifiTokenTest.java
similarity index 88%
rename from app/src/test/java/com/alphawallet/app/entity/lifi/TokenTest.java
rename to app/src/test/java/com/alphawallet/app/entity/lifi/LifiTokenTest.java
index 6c7d72a6ca..4a91c87654 100644
--- a/app/src/test/java/com/alphawallet/app/entity/lifi/TokenTest.java
+++ b/app/src/test/java/com/alphawallet/app/entity/lifi/LifiTokenTest.java
@@ -5,12 +5,12 @@
import org.junit.Test;
-public class TokenTest
+public class LifiTokenTest
{
@Test
public void getFiatValue()
{
- Token lToken = new Token();
+ LifiToken lToken = new LifiToken();
lToken.priceUSD = "6.72";
lToken.balance = "1";
@@ -20,7 +20,7 @@ public void getFiatValue()
@Test
public void getFiatValue_should_handle_exception()
{
- Token lToken = new Token();
+ LifiToken lToken = new LifiToken();
lToken.priceUSD = "6.72";
lToken.balance = "";
assertThat(lToken.getFiatValue(), equalTo(0.0));
diff --git a/app/src/test/java/com/alphawallet/app/router/CoinbasePayRouterTest.java b/app/src/test/java/com/alphawallet/app/router/CoinbasePayRouterTest.java
index 38235a88a7..ba321a6ac6 100644
--- a/app/src/test/java/com/alphawallet/app/router/CoinbasePayRouterTest.java
+++ b/app/src/test/java/com/alphawallet/app/router/CoinbasePayRouterTest.java
@@ -6,11 +6,9 @@
import android.content.Intent;
-import androidx.test.core.app.ActivityScenario;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.alphawallet.app.ui.CoinbasePayActivity;
-import com.alphawallet.app.ui.SendActivity;
import com.alphawallet.app.ui.SplashActivity;
import com.alphawallet.shadows.ShadowApp;
import com.alphawallet.shadows.ShadowKeyProviderFactory;
diff --git a/app/src/test/java/com/alphawallet/app/ui/widget/adapter/TokenFilterTest.java b/app/src/test/java/com/alphawallet/app/ui/widget/adapter/LifiTokenFilterTest.java
similarity index 69%
rename from app/src/test/java/com/alphawallet/app/ui/widget/adapter/TokenFilterTest.java
rename to app/src/test/java/com/alphawallet/app/ui/widget/adapter/LifiTokenFilterTest.java
index 84e4513f27..e43bcec822 100644
--- a/app/src/test/java/com/alphawallet/app/ui/widget/adapter/TokenFilterTest.java
+++ b/app/src/test/java/com/alphawallet/app/ui/widget/adapter/LifiTokenFilterTest.java
@@ -5,8 +5,8 @@
import androidx.annotation.NonNull;
-import com.alphawallet.app.entity.lifi.Connection;
-import com.alphawallet.app.entity.lifi.Token;
+import com.alphawallet.app.entity.lifi.LifiToken;
+import com.alphawallet.app.util.LifiTokenFilter;
import org.junit.Before;
import org.junit.Test;
@@ -14,25 +14,25 @@
import java.util.ArrayList;
import java.util.List;
-public class TokenFilterTest
+public class LifiTokenFilterTest
{
- private TokenFilter tokenFilter;
+ private LifiTokenFilter lifiTokenFilter;
@Before
public void setUp() throws Exception
{
- List list = new ArrayList<>();
+ List list = new ArrayList<>();
list.add(createToken("Ethereum", "ETH", "1"));
list.add(createToken("Solana", "SOL", "2"));
list.add(createToken("Binance", "BNB", "3"));
list.add(createToken("", "", "4"));
- tokenFilter = new TokenFilter(list);
+ lifiTokenFilter = new LifiTokenFilter(list);
}
@Test
public void nameContains()
{
- List result = tokenFilter.filterBy("an");
+ List result = lifiTokenFilter.filterBy("an");
assertThat(result.size(), equalTo(2));
assertThat(result.get(0).name, equalTo("Solana"));
assertThat(result.get(1).name, equalTo("Binance"));
@@ -41,7 +41,7 @@ public void nameContains()
@Test
public void nameStartsWith()
{
- List result = tokenFilter.filterBy("So");
+ List result = lifiTokenFilter.filterBy("So");
assertThat(result.size(), equalTo(1));
assertThat(result.get(0).name, equalTo("Solana"));
}
@@ -49,7 +49,7 @@ public void nameStartsWith()
@Test
public void symbolContains()
{
- List result = tokenFilter.filterBy("B");
+ List result = lifiTokenFilter.filterBy("B");
assertThat(result.size(), equalTo(1));
assertThat(result.get(0).name, equalTo("Binance"));
}
@@ -57,7 +57,7 @@ public void symbolContains()
@Test
public void symbolStartsWith()
{
- List result = tokenFilter.filterBy("S");
+ List result = lifiTokenFilter.filterBy("S");
assertThat(result.size(), equalTo(1));
assertThat(result.get(0).name, equalTo("Solana"));
}
@@ -65,11 +65,11 @@ public void symbolStartsWith()
@Test
public void should_be_case_insensitive()
{
- List result = tokenFilter.filterBy("s");
+ List result = lifiTokenFilter.filterBy("s");
assertThat(result.size(), equalTo(1));
assertThat(result.get(0).name, equalTo("Solana"));
- result = tokenFilter.filterBy("b");
+ result = lifiTokenFilter.filterBy("b");
assertThat(result.size(), equalTo(1));
assertThat(result.get(0).name, equalTo("Binance"));
}
@@ -77,22 +77,22 @@ public void should_be_case_insensitive()
@Test
public void should_sort_starts_with_in_front_of_contains()
{
- List list = new ArrayList<>();
+ List list = new ArrayList<>();
list.add(createToken("Solana", "SOL", "2"));
list.add(createToken("WETH", "WETH", "2"));
list.add(createToken("Ethereum", "ETH", "1"));
- tokenFilter = new TokenFilter(list);
+ lifiTokenFilter = new LifiTokenFilter(list);
- List result = tokenFilter.filterBy("eth");
+ List result = lifiTokenFilter.filterBy("eth");
assertThat(result.size(), equalTo(2));
assertThat(result.get(0).name, equalTo("Ethereum"));
assertThat(result.get(1).name, equalTo("WETH"));
}
@NonNull
- private Token createToken(String name, String symbol, String address)
+ private LifiToken createToken(String name, String symbol, String address)
{
- Token e = new Token();
+ LifiToken e = new LifiToken();
e.name = name;
e.symbol = symbol;
e.address = address;
diff --git a/app/src/test/java/com/alphawallet/app/viewmodel/TokensTest.java b/app/src/test/java/com/alphawallet/app/util/LifiTokenUtilsTest.java
similarity index 75%
rename from app/src/test/java/com/alphawallet/app/viewmodel/TokensTest.java
rename to app/src/test/java/com/alphawallet/app/util/LifiTokenUtilsTest.java
index c6a058005a..74e7fd5136 100644
--- a/app/src/test/java/com/alphawallet/app/viewmodel/TokensTest.java
+++ b/app/src/test/java/com/alphawallet/app/util/LifiTokenUtilsTest.java
@@ -1,27 +1,27 @@
-package com.alphawallet.app.viewmodel;
+package com.alphawallet.app.util;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsEqual.equalTo;
-import com.alphawallet.app.entity.lifi.Connection;
-import com.alphawallet.app.entity.lifi.Token;
+import com.alphawallet.app.entity.lifi.LifiToken;
+import com.alphawallet.app.util.LifiTokenUtils;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
-public class TokensTest
+public class LifiTokenUtilsTest
{
@Test
public void sort_token_by_fiat_value_in_DESC()
{
- List list = new ArrayList<>();
+ List list = new ArrayList<>();
list.add(createToken("Ethereum", "ETH", "0x0", 0));
list.add(createToken("Binance Smart Chain", "BNB", "0x1", 1));
list.add(createToken("Solana", "SOL", "0x2", 2));
- Tokens.sortValue(list);
+ LifiTokenUtils.sortValue(list);
assertThat(list.get(0).symbol, equalTo("SOL"));
assertThat(list.get(1).symbol, equalTo("BNB"));
@@ -31,12 +31,12 @@ public void sort_token_by_fiat_value_in_DESC()
@Test
public void sort_tokens_by_name_alphabetically()
{
- List list = new ArrayList<>();
+ List list = new ArrayList<>();
list.add(createToken("Ethereum", "ETH", "0x0", 0));
list.add(createToken("Binance Smart Chain", "BNB", "0x1", 0));
list.add(createToken("Solana", "SOL", "0x2", 0));
- Tokens.sortName(list);
+ LifiTokenUtils.sortName(list);
assertThat(list.get(0).symbol, equalTo("BNB"));
assertThat(list.get(1).symbol, equalTo("ETH"));
@@ -46,12 +46,12 @@ public void sort_tokens_by_name_alphabetically()
@Test
public void sort_name_should_return_native_token_first()
{
- List list = new ArrayList<>();
+ List list = new ArrayList<>();
list.add(createToken("Solana", "SOL", "0x0", 0));
list.add(createToken("Stox", "STX", "0x0000000000000000000000000000000000000000", 0));
list.add(createToken("stETH", "stETH", "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", 0));
- Tokens.sortName(list);
+ LifiTokenUtils.sortName(list);
assertThat(list.get(0).symbol, equalTo("stETH"));
assertThat(list.get(1).symbol, equalTo("STX"));
@@ -61,19 +61,19 @@ public void sort_name_should_return_native_token_first()
@Test
public void sort_name_should_be_case_insensitive()
{
- List list = new ArrayList<>();
+ List list = new ArrayList<>();
list.add(createToken("Stox", "STX", "0x0", 0));
list.add(createToken("stETH", "stETH", "0x3", 0));
- Tokens.sortName(list);
+ LifiTokenUtils.sortName(list);
assertThat(list.get(0).symbol, equalTo("stETH"));
assertThat(list.get(1).symbol, equalTo("STX"));
}
- private Token createToken(String name, String symbol, String address, double fiatEquivalent)
+ private LifiToken createToken(String name, String symbol, String address, double fiatEquivalent)
{
- Token lToken = new Token();
+ LifiToken lToken = new LifiToken();
lToken.name = name;
lToken.symbol = symbol;
lToken.address = address;
diff --git a/app/src/test/java/com/alphawallet/app/util/SwapUtilsTest.java b/app/src/test/java/com/alphawallet/app/util/SwapUtilsTest.java
index 4c4452d1c0..a03e9d3ca1 100644
--- a/app/src/test/java/com/alphawallet/app/util/SwapUtilsTest.java
+++ b/app/src/test/java/com/alphawallet/app/util/SwapUtilsTest.java
@@ -6,8 +6,7 @@
import com.alphawallet.app.entity.lifi.Action;
import com.alphawallet.app.entity.lifi.Estimate;
import com.alphawallet.app.entity.lifi.GasCost;
-import com.alphawallet.app.entity.lifi.Quote;
-import com.alphawallet.app.entity.lifi.Token;
+import com.alphawallet.app.entity.lifi.LifiToken;
import org.junit.Test;
@@ -21,13 +20,13 @@ public void should_return_formatted_total_gas_fees()
ArrayList gasCostList = new ArrayList<>();
GasCost gasCost1 = new GasCost();
gasCost1.amount = "1000000000000000000";
- gasCost1.token = new Token();
+ gasCost1.token = new LifiToken();
gasCost1.token.symbol = "ETH";
gasCost1.token.decimals = 18;
GasCost gasCost2 = new GasCost();
gasCost2.amount = "2000000000000000000";
- gasCost2.token = new Token();
+ gasCost2.token = new LifiToken();
gasCost2.token.symbol = "MATIC";
gasCost2.token.decimals = 18;
@@ -42,7 +41,7 @@ public void should_return_formatted_gas_fee()
{
GasCost gasCost = new GasCost();
gasCost.amount = "1000000000000000000";
- gasCost.token = new Token();
+ gasCost.token = new LifiToken();
gasCost.token.symbol = "ETH";
gasCost.token.decimals = 18;
@@ -53,7 +52,7 @@ public void should_return_formatted_gas_fee()
public void should_return_formatted_minimum_received()
{
Action action = new Action();
- action.toToken = new Token();
+ action.toToken = new LifiToken();
action.toToken.decimals = 6;
action.toToken.symbol = "ETH";
@@ -70,8 +69,8 @@ public void should_return_formatted_minimum_received()
public void should_return_formatted_current_price()
{
Action action = new Action();
- action.fromToken = new Token();
- action.toToken = new Token();
+ action.fromToken = new LifiToken();
+ action.toToken = new LifiToken();
action.fromToken.priceUSD = "2000";
action.fromToken.symbol = "ETH";
action.fromToken.decimals = 18;
diff --git a/app/src/test/java/com/alphawallet/app/util/TokenFilterTest.java b/app/src/test/java/com/alphawallet/app/util/TokenFilterTest.java
new file mode 100644
index 0000000000..03e8284518
--- /dev/null
+++ b/app/src/test/java/com/alphawallet/app/util/TokenFilterTest.java
@@ -0,0 +1,116 @@
+package com.alphawallet.app.util;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsEqual.equalTo;
+
+import androidx.annotation.NonNull;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.alphawallet.app.entity.ContractType;
+import com.alphawallet.app.entity.tokens.Token;
+import com.alphawallet.app.entity.tokens.TokenInfo;
+import com.alphawallet.shadows.ShadowApp;
+import com.alphawallet.shadows.ShadowKeyProviderFactory;
+import com.alphawallet.shadows.ShadowKeyService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@Config(shadows = {ShadowApp.class, ShadowKeyProviderFactory.class, ShadowKeyService.class})
+public class TokenFilterTest
+{
+ private TokenFilter tokenFilter;
+
+ @Before
+ public void setUp() throws Exception
+ {
+ List list = new ArrayList<>();
+ list.add(createToken("Ethereum", "ETH", "1"));
+ list.add(createToken("Solana", "SOL", "2"));
+ list.add(createToken("Binance", "BNB", "3"));
+ list.add(createToken("", "", "4"));
+ tokenFilter = new TokenFilter(list);
+ }
+
+ @Test
+ public void nameContains()
+ {
+ List result = tokenFilter.filterBy("an");
+ assertThat(result.size(), equalTo(2));
+ assertThat(result.get(0).tokenInfo.name, equalTo("Solana"));
+ assertThat(result.get(1).tokenInfo.name, equalTo("Binance"));
+ }
+
+ @Test
+ public void nameStartsWith()
+ {
+ List result = tokenFilter.filterBy("So");
+ assertThat(result.size(), equalTo(1));
+ assertThat(result.get(0).tokenInfo.name, equalTo("Solana"));
+ }
+
+ @Test
+ public void symbolContains()
+ {
+ List result = tokenFilter.filterBy("B");
+ assertThat(result.size(), equalTo(1));
+ assertThat(result.get(0).tokenInfo.name, equalTo("Binance"));
+ }
+
+ @Test
+ public void symbolStartsWith()
+ {
+ List result = tokenFilter.filterBy("S");
+ assertThat(result.size(), equalTo(1));
+ assertThat(result.get(0).tokenInfo.name, equalTo("Solana"));
+ }
+
+ @Test
+ public void should_be_case_insensitive()
+ {
+ List result = tokenFilter.filterBy("s");
+ assertThat(result.size(), equalTo(1));
+ assertThat(result.get(0).tokenInfo.name, equalTo("Solana"));
+
+ result = tokenFilter.filterBy("b");
+ assertThat(result.size(), equalTo(1));
+ assertThat(result.get(0).tokenInfo.name, equalTo("Binance"));
+ }
+
+ @Test
+ public void should_sort_starts_with_in_front_of_contains()
+ {
+ List list = new ArrayList<>();
+ list.add(createToken("Solana", "SOL", "2"));
+ list.add(createToken("WETH", "WETH", "2"));
+ list.add(createToken("Ethereum", "ETH", "1"));
+ tokenFilter = new TokenFilter(list);
+
+ List result = tokenFilter.filterBy("eth");
+ assertThat(result.size(), equalTo(2));
+ assertThat(result.get(0).tokenInfo.name, equalTo("Ethereum"));
+ assertThat(result.get(1).tokenInfo.name, equalTo("WETH"));
+ }
+
+ @NonNull
+ private Token createToken(String name, String symbol, String address)
+ {
+ TokenInfo tokenInfo =
+ new TokenInfo(
+ address,
+ name,
+ symbol,
+ 18,
+ true,
+ 1
+ );
+ return new Token(tokenInfo, BigDecimal.ZERO, System.currentTimeMillis() / 1000, "Ethereum", ContractType.ERC20);
+ }
+}
\ No newline at end of file