From d7da062b7359575c240087b4b0b0597ba8166db5 Mon Sep 17 00:00:00 2001 From: final_statics Date: Mon, 12 Jul 2021 14:53:25 +0800 Subject: [PATCH 1/8] Supports dynamic reading of ETH ABI information in external sdcrad and decode of transactions --- app/src/main/assets/abi/abiMap.json | 2 +- .../fragment/main/EthTxConfirmFragment.java | 53 +++++++-- .../ui/fragment/main/QRCodeScanFragment.java | 2 +- .../cold/viewmodel/EthTxConfirmViewModel.java | 32 +++++- .../cold/viewmodel/QrScanViewModel.java | 30 +++--- app/src/main/res/drawable-xhdpi/direction.png | Bin 0 -> 265 bytes app/src/main/res/layout/eth_tx_detail.xml | 101 ++++++++++++++++-- app/src/main/res/values-zh-rCN/strings.xml | 7 +- app/src/main/res/values/strings.xml | 5 + .../keystone/coinlib/coins/ETH/EthImpl.java | 60 +++++++++-- .../com/keystone/coinlib/v8/ScriptLoader.java | 78 +++++++++++++- 11 files changed, 327 insertions(+), 43 deletions(-) create mode 100644 app/src/main/res/drawable-xhdpi/direction.png diff --git a/app/src/main/assets/abi/abiMap.json b/app/src/main/assets/abi/abiMap.json index fdb9fadd..73d4bdb1 100644 --- a/app/src/main/assets/abi/abiMap.json +++ b/app/src/main/assets/abi/abiMap.json @@ -13,5 +13,5 @@ "0xb440dd674e1243644791a4adfe3a2abb0a92d309": "Synthetix.json", "0x448a5065aebb8e423f0896e6c5d525c040f59af3": "Maker.json", "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2": "WETH.json", - "0x111111125434b319222cdbf8c261674adb56f3ae":"1inch exchange v2.json" + "0x111111125434b319222cdbf8c261674adb56f3ae": "1inch exchange v2.json" } \ No newline at end of file diff --git a/app/src/main/java/com/keystone/cold/ui/fragment/main/EthTxConfirmFragment.java b/app/src/main/java/com/keystone/cold/ui/fragment/main/EthTxConfirmFragment.java index d6fcb733..45202232 100644 --- a/app/src/main/java/com/keystone/cold/ui/fragment/main/EthTxConfirmFragment.java +++ b/app/src/main/java/com/keystone/cold/ui/fragment/main/EthTxConfirmFragment.java @@ -19,15 +19,19 @@ package com.keystone.cold.ui.fragment.main; +import android.content.Context; +import android.content.SharedPreferences; import android.graphics.Color; import android.os.Bundle; import android.os.Handler; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.style.ForegroundColorSpan; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; +import androidx.appcompat.app.AppCompatActivity; import androidx.databinding.DataBindingUtil; import androidx.lifecycle.ViewModelProviders; @@ -48,7 +52,6 @@ import org.json.JSONObject; import java.util.List; -import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -59,10 +62,12 @@ import static com.keystone.cold.ui.fragment.setup.PreImportFragment.ACTION; public class EthTxConfirmFragment extends BaseFragment { - + private static final String VISITS_NAME = "visitsName"; + private static final String SP_NAME = "EthTxConfirmFragment"; private EthTxConfirmViewModel viewModel; private SigningDialog signingDialog; private TxEntity txEntity; + public static boolean isFromTFCard; private final Runnable forgetPassword = () -> { Bundle bundle = new Bundle(); bundle.putString(ACTION, PreImportFragment.ACTION_RESET_PWD); @@ -72,6 +77,7 @@ public class EthTxConfirmFragment extends BaseFragment { public static Pattern pattern = Pattern.compile("(?<=\\()[^\\)]+"); public static Pattern pattern1 = Pattern.compile("(?<=\\[)[^]]+"); + @Override protected int setView() { return R.layout.eth_tx_confirm; @@ -80,6 +86,7 @@ protected int setView() { @Override protected void init(View view) { Bundle data = requireArguments(); + mBinding.ethTx.checkInfo.setVisibility(View.VISIBLE); mBinding.toolbar.setNavigationOnClickListener(v -> navigateUp()); viewModel = ViewModelProviders.of(this).get(EthTxConfirmViewModel.class); try { @@ -96,6 +103,27 @@ protected void init(View view) { e.printStackTrace(); } mBinding.sign.setOnClickListener(v -> handleSign()); + mBinding.ethTx.info.setOnClickListener(view1 -> realShowDialog()); + showDialog(); + } + + private void showDialog() { + SharedPreferences sharedPreferences = mActivity.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + int visits = sharedPreferences.getInt(VISITS_NAME, 0); + if (visits++ == 0) { + realShowDialog(); + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putInt(VISITS_NAME, visits); + editor.apply(); + } + } + + private void realShowDialog() { + ModalDialog.showCommonModal((AppCompatActivity) getActivity(), + getString(R.string.tip), + getString(R.string.learn_more), + getString(R.string.know), + null); } private void handleParseException(Exception ex) { @@ -169,8 +197,12 @@ private void updateUI() { JSONObject abi = viewModel.getAbi(); if (abi != null) { updateAbiView(abi); + mBinding.ethTx.data.setVisibility(View.VISIBLE); + mBinding.ethTx.undecodeData.setVisibility(View.GONE); } else { mBinding.ethTx.data.setVisibility(View.GONE); + mBinding.ethTx.undecodeData.setVisibility(View.VISIBLE); + mBinding.ethTx.inputData.setText("0x" + viewModel.getHex()); } mBinding.ethTx.setTx(txEntity); processAndUpdateTo(); @@ -194,16 +226,20 @@ private void processAndUpdateTo() { private void updateAbiView(JSONObject abi) { if (abi != null) { try { + if (isFromTFCard) { + isFromTFCard = false; + mBinding.ethTx.tfcardTip.setVisibility(View.VISIBLE); + } String contract = abi.getString("contract"); boolean isUniswap = contract.toLowerCase().contains("uniswap"); - List itemList = new AbiItemAdapter(txEntity.getFrom(),viewModel).adapt(abi); + List itemList = new AbiItemAdapter(txEntity.getFrom(), viewModel).adapt(abi); for (AbiItemAdapter.AbiItem item : itemList) { AbiItemBinding binding = DataBindingUtil.inflate(LayoutInflater.from(mActivity), R.layout.abi_item, null, false); binding.key.setText(item.key); if (isUniswap && "to".equals(item.key)) { if (!item.value.equalsIgnoreCase(txEntity.getFrom())) { - item.value += String.format(" [%s]",getString(R.string.inconsistent_address)); + item.value += String.format(" [%s]", getString(R.string.inconsistent_address)); } binding.value.setText(highLight(item.value)); } else { @@ -221,18 +257,19 @@ private void updateAbiView(JSONObject abi) { protected void initData(Bundle savedInstanceState) { } + public static SpannableStringBuilder highLight(String content) { SpannableStringBuilder spannable = new SpannableStringBuilder(content); Matcher matcher = pattern.matcher(spannable); while (matcher.find()) - spannable.setSpan(new ForegroundColorSpan(0xff00cdc3), matcher.start() - 1 , + spannable.setSpan(new ForegroundColorSpan(0xff00cdc3), matcher.start() - 1, matcher.end() + 1, Spannable.SPAN_INCLUSIVE_INCLUSIVE); matcher = pattern1.matcher(spannable); while (matcher.find()) { - spannable.replace(matcher.start() - 1, matcher.start(),"("); - spannable.replace(matcher.end(), matcher.end() + 1,")"); - spannable.setSpan(new ForegroundColorSpan(Color.RED), matcher.start() - 1 , + spannable.replace(matcher.start() - 1, matcher.start(), "("); + spannable.replace(matcher.end(), matcher.end() + 1, ")"); + spannable.setSpan(new ForegroundColorSpan(Color.RED), matcher.start() - 1, matcher.end() + 1, Spannable.SPAN_INCLUSIVE_INCLUSIVE); } return spannable; diff --git a/app/src/main/java/com/keystone/cold/ui/fragment/main/QRCodeScanFragment.java b/app/src/main/java/com/keystone/cold/ui/fragment/main/QRCodeScanFragment.java index a1b1804e..ff79fe7d 100644 --- a/app/src/main/java/com/keystone/cold/ui/fragment/main/QRCodeScanFragment.java +++ b/app/src/main/java/com/keystone/cold/ui/fragment/main/QRCodeScanFragment.java @@ -215,7 +215,7 @@ private void handleUR(String res) { } } - private void handleException(Exception e) { + public void handleException(Exception e) { e.printStackTrace(); if (e instanceof InvalidTransactionException || e instanceof InvalidUOSException) { alert(getString(R.string.unresolve_tx), diff --git a/app/src/main/java/com/keystone/cold/viewmodel/EthTxConfirmViewModel.java b/app/src/main/java/com/keystone/cold/viewmodel/EthTxConfirmViewModel.java index e23e4a6d..c2b8fa96 100644 --- a/app/src/main/java/com/keystone/cold/viewmodel/EthTxConfirmViewModel.java +++ b/app/src/main/java/com/keystone/cold/viewmodel/EthTxConfirmViewModel.java @@ -35,6 +35,7 @@ import com.keystone.coinlib.interfaces.Signer; import com.keystone.coinlib.path.CoinPath; import com.keystone.coinlib.utils.Coins; +import com.keystone.coinlib.v8.ScriptLoader; import com.keystone.cold.AppExecutors; import com.keystone.cold.R; import com.keystone.cold.callables.ClearTokenCallable; @@ -42,6 +43,7 @@ import com.keystone.cold.db.entity.CoinEntity; import com.keystone.cold.db.entity.TxEntity; import com.keystone.cold.encryption.ChipSigner; +import com.keystone.cold.ui.fragment.main.EthTxConfirmFragment; import org.json.JSONArray; import org.json.JSONException; @@ -93,7 +95,7 @@ public JSONObject getContractMap() { return contractMap; } - public String recognizeAddress(String to){ + public String recognizeAddress(String to) { try { String addressSymbol = null; JSONArray tokensMap = getTokensMap(); @@ -104,12 +106,13 @@ public String recognizeAddress(String to){ break; } } - if (addressSymbol == null) { JSONObject bundleMap = getContractMap(); String abiFile = bundleMap.optString(to.toLowerCase()); if (!TextUtils.isEmpty(abiFile)) { addressSymbol = abiFile.replace(".json", ""); + } else { + addressSymbol = recognizeAddressFromTFCard(to); } } return addressSymbol; @@ -119,6 +122,20 @@ public String recognizeAddress(String to){ return null; } + private String recognizeAddressFromTFCard(String to) { + String addressSymbol = null; + try { + String contentFromSdCard = ScriptLoader.getContentFromSdCard(EthImpl.ABI_JSON_SDCARD_PATH, to); + if (!TextUtils.isEmpty(contentFromSdCard)) { + JSONObject sdCardJsonObject = new JSONObject(contentFromSdCard); + addressSymbol = sdCardJsonObject.optString("name"); + } + } catch (JSONException e) { + e.printStackTrace(); + } + return addressSymbol; + } + public String getNetwork(int chainId) { Network network = Network.getNetwork(chainId); if (network == null) { @@ -126,7 +143,7 @@ public String getNetwork(int chainId) { } String networkName = network.name(); if (chainId != 1) { - networkName += String.format(" (%s)",context.getString(R.string.testnet)); + networkName += String.format(" (%s)", context.getString(R.string.testnet)); } return networkName; } @@ -146,7 +163,7 @@ public void parseTxData(JSONObject object) { hdPath = object.getString("hdPath"); signId = object.getString("signId"); txHex = object.getString("txHex"); - JSONObject ethTx = EthImpl.decodeRawTransaction(txHex); + JSONObject ethTx = EthImpl.decodeRawTransaction(txHex, () -> EthTxConfirmFragment.isFromTFCard = true); if (ethTx == null) { observableTx.postValue(null); parseTxException.postValue(new InvalidTransactionException("invalid transaction")); @@ -344,6 +361,7 @@ private void signTransaction(@NonNull SignCallback callback, Signer signer) { callback.onSuccess(result.txId, result.txHex); } } + private void signMessage(@NonNull SignCallback callback, Signer signer) { if (signer == null) { callback.onFail(); @@ -360,7 +378,7 @@ private void signMessage(@NonNull SignCallback callback, Signer signer) { private Signer initSigner() { String authToken = getAuthToken(); if (TextUtils.isEmpty(authToken)) { - Log.w(TAG,"authToken null"); + Log.w(TAG, "authToken null"); return null; } return new ChipSigner(hdPath.toLowerCase(), authToken); @@ -378,6 +396,10 @@ public String getTxHex() { return Objects.requireNonNull(observableTx.getValue()).getSignedHex(); } + public String getHex() { + return txHex; + } + public int getChainId() { return chainId; } diff --git a/app/src/main/java/com/keystone/cold/viewmodel/QrScanViewModel.java b/app/src/main/java/com/keystone/cold/viewmodel/QrScanViewModel.java index b7ccd8f2..3023a4de 100644 --- a/app/src/main/java/com/keystone/cold/viewmodel/QrScanViewModel.java +++ b/app/src/main/java/com/keystone/cold/viewmodel/QrScanViewModel.java @@ -30,6 +30,7 @@ import com.keystone.coinlib.coins.ETH.EthImpl; import com.keystone.coinlib.exception.CoinNotFindException; import com.keystone.coinlib.exception.InvalidTransactionException; +import com.keystone.cold.AppExecutors; import com.keystone.cold.R; import com.keystone.cold.callables.GetMasterFingerprintCallable; import com.keystone.cold.protocol.ZipUtil; @@ -106,25 +107,30 @@ private void decodeAndProcess(JSONObject object) handleSignXrpTx(object); return; } - break; + throw new InvalidTransactionException("unknown qr code type"); case KEYSTONE: if (object.optString("type").equals("TYPE_SIGN_TX")) { handleSign(object); return; } - break; + throw new InvalidTransactionException("unknown qr code type"); case METAMASK: String txHex = object.optString("txHex"); JSONObject data = object.optJSONObject("data"); - if (!TextUtils.isEmpty(txHex) && EthImpl.decodeRawTransaction(txHex) != null) { - handleSignMetamaskTx(object); - return; - } else if (data != null) { - handleSignMetamaskMessage(object); - return; - } + AppExecutors.getInstance().diskIO().execute(() -> { + try { + if (!TextUtils.isEmpty(txHex) && EthImpl.decodeRawTransaction(txHex, null) != null) { + handleSignMetamaskTx(object); + } else if (data != null) { + handleSignMetamaskMessage(object); + } else { + throw new InvalidTransactionException("unknown qr code type"); + } + } catch (XfpNotMatchException | InvalidTransactionException e) { + AppExecutors.getInstance().mainThread().execute(() -> fragment.handleException(e)); + } + }); } - throw new InvalidTransactionException("unknown qr code type"); } private boolean checkWebAuth(JSONObject object) throws JSONException { @@ -142,7 +148,7 @@ private void handleSignMetamaskTx(JSONObject object) throws XfpNotMatchException } Bundle bundle = new Bundle(); bundle.putString(KEY_TX_DATA, object.toString()); - fragment.navigate(R.id.action_to_ethTxConfirmFragment, bundle); + AppExecutors.getInstance().mainThread().execute(() -> fragment.navigate(R.id.action_to_ethTxConfirmFragment, bundle)); } private void handleSignMetamaskMessage(JSONObject object) throws XfpNotMatchException { @@ -151,7 +157,7 @@ private void handleSignMetamaskMessage(JSONObject object) throws XfpNotMatchExce } Bundle bundle = new Bundle(); bundle.putString(KEY_TX_DATA, object.toString()); - fragment.navigate(R.id.action_to_ethSignMessageFragment, bundle); + AppExecutors.getInstance().mainThread().execute(() -> fragment.navigate(R.id.action_to_ethSignMessageFragment, bundle)); } private void handleSignXrpTx(JSONObject object) { diff --git a/app/src/main/res/drawable-xhdpi/direction.png b/app/src/main/res/drawable-xhdpi/direction.png new file mode 100644 index 0000000000000000000000000000000000000000..7537f60775bebc5bd2c59ffd288c9ff54549771a GIT binary patch literal 265 zcmeAS@N?(olHy`uVBq!ia0vp^5;?P7+2AP-{`ms{{f4h|(T znYxs-r%pXOsOTP0acfIvd_q@2 + + + + android:orientation="vertical" + android:visibility="gone"> + + + + + + + + + + + + + + + + + + + + + + + android:src="@drawable/data_bg1" /> + + android:padding="10dp" + android:paddingTop="16dp"> diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 1389a1bf..5d0e334f 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -15,7 +15,7 @@ ~ in the file COPYING. If not, see . --> - + Keystone Scan QR Code 我的铠石钱包 @@ -520,6 +520,11 @@ 您触发了“创建自己的助记词”这一高级功能,请确保您已充分了解助记词生成的相关知识后,再使用本功能。否则请关闭当前页面。 我已充分了解随机熵、BIP39、私钥生成等相关知识。 须知 + 提示 + https://keyst.one/eth 了解详情。]]> + 注意!这是一个没有解密的数据。 + (解码资源来源于TF卡) + * 请在签名之前检查交易信息 请在以下单词中任选一个作为第24个单词 请先将TF卡插入到设备中 确认是否格式化? diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6909fea3..bdfef3e9 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -553,6 +553,11 @@ You have triggered an advanced function of "Create your Recovery Phrase". Please make sure that you fully understand the dangers of this feature before proceeding to generate a recovery phrase with it. Your funds might get compromised if you fail to do so. Proceed at your own risk! I fully understand entropy, BIP39, private key generation and other related material to generate my recovery phrase. DANGER! + Note + https://keyst.one/eth for details.]]> + * Please check transaction information before signing + Attention! It’s a undecoded data. + (Decoding resources are provided by MicroSD Card) Please choose one of the following words as the 24th word Please make sure you have inserted a microSD card Are you SURE ? diff --git a/coinlib/src/main/java/com/keystone/coinlib/coins/ETH/EthImpl.java b/coinlib/src/main/java/com/keystone/coinlib/coins/ETH/EthImpl.java index 5df87aa9..4b6dd354 100644 --- a/coinlib/src/main/java/com/keystone/coinlib/coins/ETH/EthImpl.java +++ b/coinlib/src/main/java/com/keystone/coinlib/coins/ETH/EthImpl.java @@ -29,8 +29,10 @@ import com.keystone.coinlib.interfaces.SignCallback; import com.keystone.coinlib.interfaces.Signer; import com.keystone.coinlib.utils.Coins; +import com.keystone.coinlib.v8.ScriptLoader; import org.bouncycastle.util.encoders.Hex; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.web3j.abi.FunctionEncoder; @@ -49,6 +51,7 @@ import org.web3j.rlp.RlpList; import org.web3j.rlp.RlpType; +import java.io.File; import java.io.IOException; import java.math.BigInteger; import java.nio.charset.StandardCharsets; @@ -61,6 +64,7 @@ import static org.web3j.crypto.TransactionEncoder.asRlpValues; public class EthImpl implements Coin { + public static final String ABI_JSON_SDCARD_PATH = "contracts" + File.separator + "ethereum"; private final int chainId; @@ -109,7 +113,7 @@ protected RawTransaction createRawTransaction(JSONObject metaData) throws JSONEx return RawTransaction.createTransaction(nonce, gasPrice, gasLimit, to, value, data); } - public static JSONObject decodeRawTransaction(String txHex) { + public static JSONObject decodeRawTransaction(String txHex, Callback callback) { JSONObject metaData = new JSONObject(); try { RawTransaction rawTx = TransactionDecoder.decode(txHex); @@ -128,7 +132,11 @@ public static JSONObject decodeRawTransaction(String txHex) { if (!TextUtils.isEmpty(abiFile)) { abi = readAsset("abi/" + abiFile); - contractName = abiFile.replace(".json",""); + contractName = abiFile.replace(".json", ""); + } else { + abi = readAbiFromTFCard(rawTx.getTo(), callback); + contractName = contractNameFromTFCard(rawTx.getTo()); + contractName = "SwTest"; } if (TextUtils.isEmpty(abi)) { @@ -162,10 +170,46 @@ public static JSONObject decodeRawTransaction(String txHex) { return metaData; } + private static String contractNameFromTFCard(String to) { + String result = null; + try { + String contentFromSdCard = ScriptLoader.getContentFromSdCard(ABI_JSON_SDCARD_PATH, to); + if (!TextUtils.isEmpty(contentFromSdCard)) { + JSONObject sdCardJsonObject = new JSONObject(contentFromSdCard); + result = sdCardJsonObject.optString("name"); + if (TextUtils.isEmpty(result)) { + result = ""; + } + } + } catch (JSONException e) { + e.printStackTrace(); + } + return result; + } + + private static String readAbiFromTFCard(String to, Callback callback) { + String result = null; + try { + String contentFromSdCard = ScriptLoader.getContentFromSdCard(ABI_JSON_SDCARD_PATH, to); + if (!TextUtils.isEmpty(contentFromSdCard)) { + JSONObject sdCardJsonObject = new JSONObject(contentFromSdCard); + JSONObject output = sdCardJsonObject.getJSONObject("output"); + JSONArray abi = output.getJSONArray("abi"); + result = abi.toString(); + if (result != null && callback != null) { + callback.fromTFCard(); + } + } + } catch (JSONException e) { + e.printStackTrace(); + } + return result; + } + public static String getSignature(String signedHex) { SignedRawTransaction signedTx = (SignedRawTransaction) TransactionDecoder.decode(signedHex); Sign.SignatureData signatureData = signedTx.getSignatureData(); - byte[] signatureBytes = concat(concat(signatureData.getR(),signatureData.getS()),signatureData.getV()); + byte[] signatureBytes = concat(concat(signatureData.getR(), signatureData.getS()), signatureData.getV()); return Hex.toHexString(signatureBytes); } @@ -203,7 +247,7 @@ public byte[] signTransaction(RawTransaction transaction, Signer signer) { return encodeSignedTransaction(transaction, signatureData); } - public Sign.SignatureData getSignatureData(String signature) { + public Sign.SignatureData getSignatureData(String signature) { if (TextUtils.isEmpty(signature)) return null; byte[] r = Hex.decode(signature.substring(0, 64)); byte[] s = Hex.decode(signature.substring(64, 128)); @@ -240,7 +284,7 @@ public String signMessage(@NonNull String message, Signer signer) { byte[] messageHash = new StructuredDataEncoder(message).hashStructuredData(); String signature = signer.sign(Hex.toHexString(messageHash)); Sign.SignatureData signatureData = getSignatureData(signature); - byte[] sigBytes = concat(concat(signatureData.getR(),signatureData.getS()),signatureData.getV()); + byte[] sigBytes = concat(concat(signatureData.getR(), signatureData.getS()), signatureData.getV()); return Hex.toHexString(sigBytes); } catch (IOException e) { e.printStackTrace(); @@ -251,7 +295,7 @@ public String signMessage(@NonNull String message, Signer signer) { public String signPersonalMessage(@NonNull String message, Signer signer) { String signature = signer.sign(Hex.toHexString(hashPersonalMessage(message))); Sign.SignatureData signatureData = getSignatureData(signature); - byte[] sigBytes = concat(concat(signatureData.getR(),signatureData.getS()),signatureData.getV()); + byte[] sigBytes = concat(concat(signatureData.getR(), signatureData.getS()), signatureData.getV()); return Hex.toHexString(sigBytes); } @@ -269,4 +313,8 @@ public String generateAddress(@NonNull String publicKey) { public boolean isAddressValid(@NonNull String address) { return false; } + + public interface Callback { + void fromTFCard(); + } } diff --git a/coinlib/src/main/java/com/keystone/coinlib/v8/ScriptLoader.java b/coinlib/src/main/java/com/keystone/coinlib/v8/ScriptLoader.java index e4c8f356..2726db7b 100644 --- a/coinlib/src/main/java/com/keystone/coinlib/v8/ScriptLoader.java +++ b/coinlib/src/main/java/com/keystone/coinlib/v8/ScriptLoader.java @@ -16,21 +16,31 @@ */ package com.keystone.coinlib.v8; + +import android.content.Context; import android.content.res.AssetManager; +import android.os.storage.StorageManager; +import android.os.storage.StorageVolume; import android.text.TextUtils; +import android.util.Log; -import com.keystone.coinlib.Coinlib; import com.eclipsesource.v8.V8; +import com.keystone.coinlib.Coinlib; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.lang.reflect.Method; +import java.util.List; public class ScriptLoader { + private static final String TAG = "ScriptLoader"; public static ScriptLoader sInstance; @@ -83,4 +93,70 @@ public static String readAsset(String fileName) { } return stringBuilder.toString(); } + + public static String getContentFromSdCard(String path, String fileName) { + if (TextUtils.isEmpty(externalSDCardPath())) { + Log.d(TAG, "sdCard is not exists"); + return ""; + } + File file = new File(externalSDCardPath() + File.separator + path, fileName + ".json"); + if (!file.exists()) { + Log.d(TAG, file.getAbsolutePath() + " is not exists"); + return ""; + } + StringBuilder stringBuilder = new StringBuilder(); + Log.d(TAG, file.getAbsolutePath() + " is exists"); + BufferedReader bfr = null; + try { + bfr = new BufferedReader(new FileReader(file)); + String line = bfr.readLine(); + while (line != null) { + stringBuilder.append(line); + stringBuilder.append("\n"); + line = bfr.readLine(); + } + Log.d(TAG, "bufferRead: " + stringBuilder.toString()); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (bfr != null) { + try { + bfr.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return stringBuilder.toString(); + } + + private static String externalSDCardPath() { + String sdCardPath = ""; + try { + StorageManager storageManager = (StorageManager) Coinlib.sInstance.getContext().getSystemService(Context.STORAGE_SERVICE); + // 7.0才有的方法 + List storageVolumes = storageManager.getStorageVolumes(); + Class volumeClass = Class.forName("android.os.storage.StorageVolume"); + Method getPath = volumeClass.getDeclaredMethod("getPath"); + Method isRemovable = volumeClass.getDeclaredMethod("isRemovable"); + getPath.setAccessible(true); + isRemovable.setAccessible(true); + StorageVolume storageVolume = storageVolumes.get(storageVolumes.size() - 1); + sdCardPath = (String) getPath.invoke(storageVolume); + Boolean isRemove = (Boolean) isRemovable.invoke(storageVolume); + if (storageVolumes.size() > 1) { + Log.d(TAG, "externalSDCardPath is === " + sdCardPath); + Log.d(TAG, "isRemoveble == " + isRemove); + } else { + Log.d(TAG, "Built-in sd card path is === " + sdCardPath); + Log.d(TAG, "isRemoveble == " + isRemove); + Log.d(TAG, "no sd card inserted"); + return ""; + } + } catch (Exception e) { + e.printStackTrace(); + } + return sdCardPath; + } + } From 523d327d3dacfa1d6e6aecfc2f6f4acd549c2d3d Mon Sep 17 00:00:00 2001 From: caixiao-QA Date: Mon, 12 Jul 2021 15:46:11 +0800 Subject: [PATCH 2/8] update version --- app/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/version.properties b/app/version.properties index 36385016..52a1df96 100644 --- a/app/version.properties +++ b/app/version.properties @@ -17,5 +17,5 @@ #Fri Feb 21 13:33:46 CST 2020 major=1 -minor=1 +minor=2 patch=0 From 84e438c170abd4de5650977b1d95f1a0398f28e7 Mon Sep 17 00:00:00 2001 From: final_statics Date: Tue, 13 Jul 2021 13:18:20 +0800 Subject: [PATCH 3/8] Modify the code specification to supplement the display of undecoded data of the signature details --- .../java/com/keystone/cold/Utilities.java | 12 ++++++++++ .../fragment/main/EthTxConfirmFragment.java | 20 ++++++---------- .../cold/ui/fragment/main/EthTxFragment.java | 24 ++++++++++++++++++- .../cold/viewmodel/EthTxConfirmViewModel.java | 14 +++++++---- app/src/main/res/layout/eth_tx_detail.xml | 8 +++---- app/src/main/res/values-zh-rCN/strings.xml | 4 ++-- app/src/main/res/values/colors.xml | 1 + app/src/main/res/values/strings.xml | 2 +- .../keystone/coinlib/coins/ETH/EthImpl.java | 1 - .../com/keystone/coinlib/v8/ScriptLoader.java | 20 +++------------- 10 files changed, 62 insertions(+), 44 deletions(-) diff --git a/app/src/main/java/com/keystone/cold/Utilities.java b/app/src/main/java/com/keystone/cold/Utilities.java index f7e0d6ec..d8ec54f2 100644 --- a/app/src/main/java/com/keystone/cold/Utilities.java +++ b/app/src/main/java/com/keystone/cold/Utilities.java @@ -31,6 +31,7 @@ import com.keystone.cold.ui.modal.ModalDialog; import static android.content.Context.MODE_PRIVATE; +import static com.keystone.cold.ui.fragment.main.EthTxConfirmFragment.PREFERENCE_KEY_VISITS; import static com.keystone.cold.ui.fragment.setting.FingerprintPreferenceFragment.FINGERPRINT_UNLOCK; public class Utilities { @@ -57,6 +58,7 @@ public class Utilities { public static final String ATTACK_DETECTED = "attack_detected"; public static final String INPUT_SETTINGS_CLEARED = "input_settings_cleared"; + public static void alert(AppCompatActivity activity, @Nullable String title, @NonNull String message, String buttonText, Runnable action) { @@ -212,4 +214,14 @@ public static boolean isAttackDetected(Context context) { SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE); return sp.getBoolean(ATTACK_DETECTED,false); } + + public static int getVisitsTimes(Context context) { + SharedPreferences sp = context.getSharedPreferences(SHARED_PREFERENCES_KEY, MODE_PRIVATE); + return sp.getInt(PREFERENCE_KEY_VISITS, 0); + } + + public static void setVisitsTimes(Context context, int visits) { + SharedPreferences sp = context.getSharedPreferences(SHARED_PREFERENCES_KEY, MODE_PRIVATE); + sp.edit().putInt(PREFERENCE_KEY_VISITS, visits).apply(); + } } diff --git a/app/src/main/java/com/keystone/cold/ui/fragment/main/EthTxConfirmFragment.java b/app/src/main/java/com/keystone/cold/ui/fragment/main/EthTxConfirmFragment.java index 45202232..d2cd0185 100644 --- a/app/src/main/java/com/keystone/cold/ui/fragment/main/EthTxConfirmFragment.java +++ b/app/src/main/java/com/keystone/cold/ui/fragment/main/EthTxConfirmFragment.java @@ -19,15 +19,12 @@ package com.keystone.cold.ui.fragment.main; -import android.content.Context; -import android.content.SharedPreferences; import android.graphics.Color; import android.os.Bundle; import android.os.Handler; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.style.ForegroundColorSpan; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -36,6 +33,7 @@ import androidx.lifecycle.ViewModelProviders; import com.keystone.cold.R; +import com.keystone.cold.Utilities; import com.keystone.cold.callables.FingerprintPolicyCallable; import com.keystone.cold.databinding.AbiItemBinding; import com.keystone.cold.databinding.EthTxConfirmBinding; @@ -62,8 +60,7 @@ import static com.keystone.cold.ui.fragment.setup.PreImportFragment.ACTION; public class EthTxConfirmFragment extends BaseFragment { - private static final String VISITS_NAME = "visitsName"; - private static final String SP_NAME = "EthTxConfirmFragment"; + public static final String PREFERENCE_KEY_VISITS = "visits_times"; private EthTxConfirmViewModel viewModel; private SigningDialog signingDialog; private TxEntity txEntity; @@ -108,13 +105,10 @@ protected void init(View view) { } private void showDialog() { - SharedPreferences sharedPreferences = mActivity.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); - int visits = sharedPreferences.getInt(VISITS_NAME, 0); + int visits = Utilities.getVisitsTimes(mActivity); if (visits++ == 0) { realShowDialog(); - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putInt(VISITS_NAME, visits); - editor.apply(); + Utilities.setVisitsTimes(mActivity, visits); } } @@ -198,11 +192,11 @@ private void updateUI() { if (abi != null) { updateAbiView(abi); mBinding.ethTx.data.setVisibility(View.VISIBLE); - mBinding.ethTx.undecodeData.setVisibility(View.GONE); + mBinding.ethTx.undecodedData.setVisibility(View.GONE); } else { mBinding.ethTx.data.setVisibility(View.GONE); - mBinding.ethTx.undecodeData.setVisibility(View.VISIBLE); - mBinding.ethTx.inputData.setText("0x" + viewModel.getHex()); + mBinding.ethTx.undecodedData.setVisibility(View.VISIBLE); + mBinding.ethTx.inputData.setText("0x" + viewModel.getInputData()); } mBinding.ethTx.setTx(txEntity); processAndUpdateTo(); diff --git a/app/src/main/java/com/keystone/cold/ui/fragment/main/EthTxFragment.java b/app/src/main/java/com/keystone/cold/ui/fragment/main/EthTxFragment.java index fe735094..370ebf3a 100644 --- a/app/src/main/java/com/keystone/cold/ui/fragment/main/EthTxFragment.java +++ b/app/src/main/java/com/keystone/cold/ui/fragment/main/EthTxFragment.java @@ -24,6 +24,7 @@ import android.view.LayoutInflater; import android.view.View; +import androidx.appcompat.app.AppCompatActivity; import androidx.databinding.DataBindingUtil; import androidx.lifecycle.ViewModelProviders; @@ -32,6 +33,7 @@ import com.keystone.cold.databinding.EthTxBinding; import com.keystone.cold.db.entity.TxEntity; import com.keystone.cold.ui.fragment.BaseFragment; +import com.keystone.cold.ui.modal.ModalDialog; import com.keystone.cold.viewmodel.CoinListViewModel; import com.keystone.cold.viewmodel.EthTxConfirmViewModel; import com.keystone.cold.viewmodel.WatchWallet; @@ -42,7 +44,6 @@ import java.nio.charset.StandardCharsets; import java.util.List; -import java.util.Objects; import static com.keystone.cold.ui.fragment.main.EthTxConfirmFragment.highLight; import static com.keystone.cold.ui.fragment.main.TxFragment.KEY_TX_ID; @@ -71,6 +72,15 @@ protected void init(View view) { } }); viewModel = ViewModelProviders.of(this).get(EthTxConfirmViewModel.class); + mBinding.ethTx.info.setOnClickListener(view1 -> showDialog()); + } + + private void showDialog() { + ModalDialog.showCommonModal((AppCompatActivity) getActivity(), + getString(R.string.tip), + getString(R.string.learn_more), + getString(R.string.know), + null); } private void updateUI() { @@ -120,11 +130,23 @@ private void updateAbiView(JSONObject abi) { } mBinding.ethTx.container.addView(binding.getRoot()); } + mBinding.ethTx.data.setVisibility(View.VISIBLE); + mBinding.ethTx.undecodedData.setVisibility(View.GONE); } catch (JSONException e) { e.printStackTrace(); } } else { mBinding.ethTx.data.setVisibility(View.GONE); + mBinding.ethTx.undecodedData.setVisibility(View.VISIBLE); + JSONObject signData = null; + try { + signData = new JSONObject(txEntity.getSignedHex()); + } catch (JSONException e) { + e.printStackTrace(); + } + if (signData != null) { + mBinding.ethTx.inputData.setText("0x" + signData.optString("inputData")); + } } } diff --git a/app/src/main/java/com/keystone/cold/viewmodel/EthTxConfirmViewModel.java b/app/src/main/java/com/keystone/cold/viewmodel/EthTxConfirmViewModel.java index c2b8fa96..dc1dd414 100644 --- a/app/src/main/java/com/keystone/cold/viewmodel/EthTxConfirmViewModel.java +++ b/app/src/main/java/com/keystone/cold/viewmodel/EthTxConfirmViewModel.java @@ -71,6 +71,7 @@ public class EthTxConfirmViewModel extends TxConfirmViewModel { private String messageData; private String messageSignature; private String fromAddress; + private String inputData; public EthTxConfirmViewModel(@NonNull Application application) { super(application); @@ -173,7 +174,9 @@ public void parseTxData(JSONObject object) { String data = ethTx.getString("data"); try { abi = new JSONObject(data); - } catch (JSONException ignore) { } + } catch (JSONException ignore) { + inputData = data; + } TxEntity tx = generateTxEntity(ethTx); observableTx.postValue(tx); @@ -341,6 +344,7 @@ protected TxEntity onSignSuccess(String txId, String rawTx) { signed.put("signId", signId); signed.put("chainId", chainId); signed.put("abi", abi); + signed.put("inputData", inputData); tx.setSignedHex(signed.toString()); } catch (JSONException e) { e.printStackTrace(); @@ -396,11 +400,11 @@ public String getTxHex() { return Objects.requireNonNull(observableTx.getValue()).getSignedHex(); } - public String getHex() { - return txHex; - } - public int getChainId() { return chainId; } + + public String getInputData() { + return inputData; + } } diff --git a/app/src/main/res/layout/eth_tx_detail.xml b/app/src/main/res/layout/eth_tx_detail.xml index 69a95622..6aec6520 100644 --- a/app/src/main/res/layout/eth_tx_detail.xml +++ b/app/src/main/res/layout/eth_tx_detail.xml @@ -286,7 +286,7 @@ . --> - + Keystone Scan QR Code 我的铠石钱包 @@ -522,7 +522,7 @@ 须知 提示 https://keyst.one/eth 了解详情。]]> - 注意!这是一个没有解密的数据。 + 注意!这是一个没有解析的数据。 (解码资源来源于TF卡) * 请在签名之前检查交易信息 请在以下单词中任选一个作为第24个单词 diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index dacd2dc4..097cef72 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -38,4 +38,5 @@ #000000 #FF874B #ff0000 + #FF0B0B diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bdfef3e9..21920c85 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -556,7 +556,7 @@ Note https://keyst.one/eth for details.]]> * Please check transaction information before signing - Attention! It’s a undecoded data. + Attention! It’s a undecoded data. (Decoding resources are provided by MicroSD Card) Please choose one of the following words as the 24th word Please make sure you have inserted a microSD card diff --git a/coinlib/src/main/java/com/keystone/coinlib/coins/ETH/EthImpl.java b/coinlib/src/main/java/com/keystone/coinlib/coins/ETH/EthImpl.java index 4b6dd354..900dc6c7 100644 --- a/coinlib/src/main/java/com/keystone/coinlib/coins/ETH/EthImpl.java +++ b/coinlib/src/main/java/com/keystone/coinlib/coins/ETH/EthImpl.java @@ -136,7 +136,6 @@ public static JSONObject decodeRawTransaction(String txHex, Callback callback) { } else { abi = readAbiFromTFCard(rawTx.getTo(), callback); contractName = contractNameFromTFCard(rawTx.getTo()); - contractName = "SwTest"; } if (TextUtils.isEmpty(abi)) { diff --git a/coinlib/src/main/java/com/keystone/coinlib/v8/ScriptLoader.java b/coinlib/src/main/java/com/keystone/coinlib/v8/ScriptLoader.java index 2726db7b..374e881a 100644 --- a/coinlib/src/main/java/com/keystone/coinlib/v8/ScriptLoader.java +++ b/coinlib/src/main/java/com/keystone/coinlib/v8/ScriptLoader.java @@ -96,16 +96,14 @@ public static String readAsset(String fileName) { public static String getContentFromSdCard(String path, String fileName) { if (TextUtils.isEmpty(externalSDCardPath())) { - Log.d(TAG, "sdCard is not exists"); + Log.i(TAG, "sdCard is not exists"); return ""; } File file = new File(externalSDCardPath() + File.separator + path, fileName + ".json"); if (!file.exists()) { - Log.d(TAG, file.getAbsolutePath() + " is not exists"); return ""; } StringBuilder stringBuilder = new StringBuilder(); - Log.d(TAG, file.getAbsolutePath() + " is exists"); BufferedReader bfr = null; try { bfr = new BufferedReader(new FileReader(file)); @@ -115,7 +113,6 @@ public static String getContentFromSdCard(String path, String fileName) { stringBuilder.append("\n"); line = bfr.readLine(); } - Log.d(TAG, "bufferRead: " + stringBuilder.toString()); } catch (IOException e) { e.printStackTrace(); } finally { @@ -134,25 +131,14 @@ private static String externalSDCardPath() { String sdCardPath = ""; try { StorageManager storageManager = (StorageManager) Coinlib.sInstance.getContext().getSystemService(Context.STORAGE_SERVICE); - // 7.0才有的方法 + + // Android N started to have this method List storageVolumes = storageManager.getStorageVolumes(); Class volumeClass = Class.forName("android.os.storage.StorageVolume"); Method getPath = volumeClass.getDeclaredMethod("getPath"); - Method isRemovable = volumeClass.getDeclaredMethod("isRemovable"); getPath.setAccessible(true); - isRemovable.setAccessible(true); StorageVolume storageVolume = storageVolumes.get(storageVolumes.size() - 1); sdCardPath = (String) getPath.invoke(storageVolume); - Boolean isRemove = (Boolean) isRemovable.invoke(storageVolume); - if (storageVolumes.size() > 1) { - Log.d(TAG, "externalSDCardPath is === " + sdCardPath); - Log.d(TAG, "isRemoveble == " + isRemove); - } else { - Log.d(TAG, "Built-in sd card path is === " + sdCardPath); - Log.d(TAG, "isRemoveble == " + isRemove); - Log.d(TAG, "no sd card inserted"); - return ""; - } } catch (Exception e) { e.printStackTrace(); } From 8da148c2e04a4e4b043e52cf2bb5a1438f109e57 Mon Sep 17 00:00:00 2001 From: final_statics Date: Tue, 13 Jul 2021 13:49:11 +0800 Subject: [PATCH 4/8] UI adjustment --- app/src/main/res/layout/eth_tx_detail.xml | 14 +++++++++----- app/src/main/res/values/colors.xml | 1 + 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/layout/eth_tx_detail.xml b/app/src/main/res/layout/eth_tx_detail.xml index 6aec6520..9f9c5eb5 100644 --- a/app/src/main/res/layout/eth_tx_detail.xml +++ b/app/src/main/res/layout/eth_tx_detail.xml @@ -121,8 +121,11 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/check_info" - android:textColor="#FBC31B" - android:gravity="center_horizontal" + android:textColor="@color/check_info_color" + android:textSize="13sp" + android:layout_marginStart="16dp" + android:layout_marginTop="8dp" + android:gravity="start" android:visibility="gone"/> @@ -370,7 +374,7 @@ android:layout_marginStart="5dp" android:text="@string/from_tfcard" android:textColor="@color/white" - android:textSize="14sp" + android:textSize="13sp" android:visibility="gone"/> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 097cef72..6076bf93 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -39,4 +39,5 @@ #FF874B #ff0000 #FF0B0B + #FBC31B From 5155c85f3cf8bf70cdbedb54ec4a71c400c956e9 Mon Sep 17 00:00:00 2001 From: caixiao-QA Date: Tue, 13 Jul 2021 14:16:58 +0800 Subject: [PATCH 5/8] update ETH tx strings --- app/src/main/res/values-zh-rCN/strings.xml | 4 ++-- app/src/main/res/values/strings.xml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index f69606ad..c2fce234 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -522,9 +522,9 @@ 须知 提示 https://keyst.one/eth 了解详情。]]> - 注意!这是一个没有解析的数据。 + 注意!该数据未被解析。 (解码资源来源于TF卡) - * 请在签名之前检查交易信息 + * 签名前,请仔细检查交易详情 请在以下单词中任选一个作为第24个单词 请先将TF卡插入到设备中 确认是否格式化? diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 21920c85..78e3cbf3 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -554,9 +554,9 @@ I fully understand entropy, BIP39, private key generation and other related material to generate my recovery phrase. DANGER! Note - https://keyst.one/eth for details.]]> - * Please check transaction information before signing - Attention! It’s a undecoded data. + https://keyst.one/eth for details.]]> + * Please check transaction details before signing + Attention! It’s a undecoded data. (Decoding resources are provided by MicroSD Card) Please choose one of the following words as the 24th word Please make sure you have inserted a microSD card From b2afbded3d5fd15ac05857c15427d7625c1671fa Mon Sep 17 00:00:00 2001 From: caixiao-QA Date: Tue, 13 Jul 2021 14:28:57 +0800 Subject: [PATCH 6/8] update ETH tx strings --- app/src/main/res/values-zh-rCN/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index c2fce234..c24a6e7a 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -522,7 +522,7 @@ 须知 提示 https://keyst.one/eth 了解详情。]]> - 注意!该数据未被解析。 + 注意!该数据未被解析。 (解码资源来源于TF卡) * 签名前,请仔细检查交易详情 请在以下单词中任选一个作为第24个单词 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 78e3cbf3..fb47a72e 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -556,7 +556,7 @@ Note https://keyst.one/eth for details.]]> * Please check transaction details before signing - Attention! It’s a undecoded data. + Attention! It’s a undecoded data. (Decoding resources are provided by MicroSD Card) Please choose one of the following words as the 24th word Please make sure you have inserted a microSD card From 42d7eb5303bdd5970d268f52903cb5e09d829a01 Mon Sep 17 00:00:00 2001 From: final_statics Date: Tue, 13 Jul 2021 14:37:02 +0800 Subject: [PATCH 7/8] Optimize the code, modify the parse json path --- .../fragment/main/EthTxConfirmFragment.java | 3 +- .../cold/viewmodel/EthTxConfirmViewModel.java | 4 +- .../keystone/coinlib/coins/ETH/EthImpl.java | 9 +-- .../com/keystone/coinlib/utils/AbiLoader.java | 71 +++++++++++++++++++ .../com/keystone/coinlib/v8/ScriptLoader.java | 59 --------------- 5 files changed, 80 insertions(+), 66 deletions(-) create mode 100644 coinlib/src/main/java/com/keystone/coinlib/utils/AbiLoader.java diff --git a/app/src/main/java/com/keystone/cold/ui/fragment/main/EthTxConfirmFragment.java b/app/src/main/java/com/keystone/cold/ui/fragment/main/EthTxConfirmFragment.java index d2cd0185..5ac72503 100644 --- a/app/src/main/java/com/keystone/cold/ui/fragment/main/EthTxConfirmFragment.java +++ b/app/src/main/java/com/keystone/cold/ui/fragment/main/EthTxConfirmFragment.java @@ -32,6 +32,7 @@ import androidx.databinding.DataBindingUtil; import androidx.lifecycle.ViewModelProviders; +import com.keystone.cold.MainApplication; import com.keystone.cold.R; import com.keystone.cold.Utilities; import com.keystone.cold.callables.FingerprintPolicyCallable; @@ -256,7 +257,7 @@ public static SpannableStringBuilder highLight(String content) { SpannableStringBuilder spannable = new SpannableStringBuilder(content); Matcher matcher = pattern.matcher(spannable); while (matcher.find()) - spannable.setSpan(new ForegroundColorSpan(0xff00cdc3), matcher.start() - 1, + spannable.setSpan(new ForegroundColorSpan(MainApplication.getApplication().getColor(R.color.icon_select)), matcher.start() - 1, matcher.end() + 1, Spannable.SPAN_INCLUSIVE_INCLUSIVE); matcher = pattern1.matcher(spannable); diff --git a/app/src/main/java/com/keystone/cold/viewmodel/EthTxConfirmViewModel.java b/app/src/main/java/com/keystone/cold/viewmodel/EthTxConfirmViewModel.java index dc1dd414..dc9b6fee 100644 --- a/app/src/main/java/com/keystone/cold/viewmodel/EthTxConfirmViewModel.java +++ b/app/src/main/java/com/keystone/cold/viewmodel/EthTxConfirmViewModel.java @@ -34,8 +34,8 @@ import com.keystone.coinlib.interfaces.SignCallback; import com.keystone.coinlib.interfaces.Signer; import com.keystone.coinlib.path.CoinPath; +import com.keystone.coinlib.utils.AbiLoader; import com.keystone.coinlib.utils.Coins; -import com.keystone.coinlib.v8.ScriptLoader; import com.keystone.cold.AppExecutors; import com.keystone.cold.R; import com.keystone.cold.callables.ClearTokenCallable; @@ -126,7 +126,7 @@ public String recognizeAddress(String to) { private String recognizeAddressFromTFCard(String to) { String addressSymbol = null; try { - String contentFromSdCard = ScriptLoader.getContentFromSdCard(EthImpl.ABI_JSON_SDCARD_PATH, to); + String contentFromSdCard = AbiLoader.getContentFromSdCard(EthImpl.ABI_JSON_SDCARD_PATH, to); if (!TextUtils.isEmpty(contentFromSdCard)) { JSONObject sdCardJsonObject = new JSONObject(contentFromSdCard); addressSymbol = sdCardJsonObject.optString("name"); diff --git a/coinlib/src/main/java/com/keystone/coinlib/coins/ETH/EthImpl.java b/coinlib/src/main/java/com/keystone/coinlib/coins/ETH/EthImpl.java index 900dc6c7..3a7640c1 100644 --- a/coinlib/src/main/java/com/keystone/coinlib/coins/ETH/EthImpl.java +++ b/coinlib/src/main/java/com/keystone/coinlib/coins/ETH/EthImpl.java @@ -28,8 +28,8 @@ import com.keystone.coinlib.interfaces.Coin; import com.keystone.coinlib.interfaces.SignCallback; import com.keystone.coinlib.interfaces.Signer; +import com.keystone.coinlib.utils.AbiLoader; import com.keystone.coinlib.utils.Coins; -import com.keystone.coinlib.v8.ScriptLoader; import org.bouncycastle.util.encoders.Hex; import org.json.JSONArray; @@ -172,7 +172,7 @@ public static JSONObject decodeRawTransaction(String txHex, Callback callback) { private static String contractNameFromTFCard(String to) { String result = null; try { - String contentFromSdCard = ScriptLoader.getContentFromSdCard(ABI_JSON_SDCARD_PATH, to); + String contentFromSdCard = AbiLoader.getContentFromSdCard(ABI_JSON_SDCARD_PATH, to); if (!TextUtils.isEmpty(contentFromSdCard)) { JSONObject sdCardJsonObject = new JSONObject(contentFromSdCard); result = sdCardJsonObject.optString("name"); @@ -189,10 +189,11 @@ private static String contractNameFromTFCard(String to) { private static String readAbiFromTFCard(String to, Callback callback) { String result = null; try { - String contentFromSdCard = ScriptLoader.getContentFromSdCard(ABI_JSON_SDCARD_PATH, to); + String contentFromSdCard = AbiLoader.getContentFromSdCard(ABI_JSON_SDCARD_PATH, to); if (!TextUtils.isEmpty(contentFromSdCard)) { JSONObject sdCardJsonObject = new JSONObject(contentFromSdCard); - JSONObject output = sdCardJsonObject.getJSONObject("output"); + JSONObject metadata = sdCardJsonObject.getJSONObject("metadata"); + JSONObject output = metadata.getJSONObject("output"); JSONArray abi = output.getJSONArray("abi"); result = abi.toString(); if (result != null && callback != null) { diff --git a/coinlib/src/main/java/com/keystone/coinlib/utils/AbiLoader.java b/coinlib/src/main/java/com/keystone/coinlib/utils/AbiLoader.java new file mode 100644 index 00000000..33c7c823 --- /dev/null +++ b/coinlib/src/main/java/com/keystone/coinlib/utils/AbiLoader.java @@ -0,0 +1,71 @@ +package com.keystone.coinlib.utils; + +import android.content.Context; +import android.os.storage.StorageManager; +import android.os.storage.StorageVolume; +import android.text.TextUtils; +import android.util.Log; + +import com.keystone.coinlib.Coinlib; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.List; + +public class AbiLoader { + private static final String TAG = "AbiLoader"; + + public static String getContentFromSdCard(String path, String fileName) { + if (TextUtils.isEmpty(externalSDCardPath())) { + Log.i(TAG, "sdCard is not exists"); + return ""; + } + File file = new File(externalSDCardPath() + File.separator + path, fileName + ".json"); + if (!file.exists()) { + return ""; + } + StringBuilder stringBuilder = new StringBuilder(); + BufferedReader bfr = null; + try { + bfr = new BufferedReader(new FileReader(file)); + String line = bfr.readLine(); + while (line != null) { + stringBuilder.append(line); + stringBuilder.append("\n"); + line = bfr.readLine(); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (bfr != null) { + try { + bfr.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return stringBuilder.toString(); + } + + private static String externalSDCardPath() { + String sdCardPath = ""; + try { + StorageManager storageManager = (StorageManager) Coinlib.sInstance.getContext().getSystemService(Context.STORAGE_SERVICE); + + // Android N started to have this method + List storageVolumes = storageManager.getStorageVolumes(); + Class volumeClass = Class.forName("android.os.storage.StorageVolume"); + Method getPath = volumeClass.getDeclaredMethod("getPath"); + getPath.setAccessible(true); + StorageVolume storageVolume = storageVolumes.get(storageVolumes.size() - 1); + sdCardPath = (String) getPath.invoke(storageVolume); + } catch (Exception e) { + e.printStackTrace(); + } + return sdCardPath; + } +} diff --git a/coinlib/src/main/java/com/keystone/coinlib/v8/ScriptLoader.java b/coinlib/src/main/java/com/keystone/coinlib/v8/ScriptLoader.java index 374e881a..c62ef0d9 100644 --- a/coinlib/src/main/java/com/keystone/coinlib/v8/ScriptLoader.java +++ b/coinlib/src/main/java/com/keystone/coinlib/v8/ScriptLoader.java @@ -17,12 +17,8 @@ package com.keystone.coinlib.v8; -import android.content.Context; import android.content.res.AssetManager; -import android.os.storage.StorageManager; -import android.os.storage.StorageVolume; import android.text.TextUtils; -import android.util.Log; import com.eclipsesource.v8.V8; import com.keystone.coinlib.Coinlib; @@ -31,13 +27,9 @@ import org.json.JSONObject; import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.lang.reflect.Method; -import java.util.List; public class ScriptLoader { private static final String TAG = "ScriptLoader"; @@ -94,55 +86,4 @@ public static String readAsset(String fileName) { return stringBuilder.toString(); } - public static String getContentFromSdCard(String path, String fileName) { - if (TextUtils.isEmpty(externalSDCardPath())) { - Log.i(TAG, "sdCard is not exists"); - return ""; - } - File file = new File(externalSDCardPath() + File.separator + path, fileName + ".json"); - if (!file.exists()) { - return ""; - } - StringBuilder stringBuilder = new StringBuilder(); - BufferedReader bfr = null; - try { - bfr = new BufferedReader(new FileReader(file)); - String line = bfr.readLine(); - while (line != null) { - stringBuilder.append(line); - stringBuilder.append("\n"); - line = bfr.readLine(); - } - } catch (IOException e) { - e.printStackTrace(); - } finally { - if (bfr != null) { - try { - bfr.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - return stringBuilder.toString(); - } - - private static String externalSDCardPath() { - String sdCardPath = ""; - try { - StorageManager storageManager = (StorageManager) Coinlib.sInstance.getContext().getSystemService(Context.STORAGE_SERVICE); - - // Android N started to have this method - List storageVolumes = storageManager.getStorageVolumes(); - Class volumeClass = Class.forName("android.os.storage.StorageVolume"); - Method getPath = volumeClass.getDeclaredMethod("getPath"); - getPath.setAccessible(true); - StorageVolume storageVolume = storageVolumes.get(storageVolumes.size() - 1); - sdCardPath = (String) getPath.invoke(storageVolume); - } catch (Exception e) { - e.printStackTrace(); - } - return sdCardPath; - } - } From 0639e9bc339a023e5c811823119e5b720bf0ef75 Mon Sep 17 00:00:00 2001 From: final_statics Date: Tue, 13 Jul 2021 15:06:23 +0800 Subject: [PATCH 8/8] UI adjustment --- app/src/main/res/layout/eth_tx_detail.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/layout/eth_tx_detail.xml b/app/src/main/res/layout/eth_tx_detail.xml index 9f9c5eb5..e8cea67b 100644 --- a/app/src/main/res/layout/eth_tx_detail.xml +++ b/app/src/main/res/layout/eth_tx_detail.xml @@ -372,6 +372,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="5dp" + android:layout_marginEnd="16dp" android:text="@string/from_tfcard" android:textColor="@color/white" android:textSize="13sp"