mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-04 17:28:42 +02:00
Compare commits
36 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2c3e73b540 | ||
![]() |
830d9dadb9 | ||
![]() |
331d88ebba | ||
![]() |
7cc2f6fafb | ||
![]() |
9a3ee0eda8 | ||
![]() |
6e898939a3 | ||
![]() |
40ae39d647 | ||
![]() |
ca81e652e5 | ||
![]() |
796048be4e | ||
![]() |
441bf995c8 | ||
![]() |
e8860ab8eb | ||
![]() |
525b38ff53 | ||
![]() |
ba79bf87aa | ||
![]() |
3fe6571e7d | ||
![]() |
364e6a8137 | ||
![]() |
cb69ce99d6 | ||
![]() |
1f976872fc | ||
![]() |
27f266b6f7 | ||
![]() |
168928d54a | ||
![]() |
b3f61072aa | ||
![]() |
ccb64aded0 | ||
![]() |
e98fa089f2 | ||
![]() |
884878b7a7 | ||
![]() |
4e23f0ef3a | ||
![]() |
6ea4e3d998 | ||
![]() |
971c90f35b | ||
![]() |
f0523c403c | ||
![]() |
966ed23b87 | ||
![]() |
95f2ca74a6 | ||
![]() |
81d94478f2 | ||
![]() |
16ff779ebc | ||
![]() |
6b7bb164f4 | ||
![]() |
da1d4ea1bf | ||
![]() |
d5a967f690 | ||
![]() |
de8de02f9f | ||
![]() |
06456e33e4 |
@@ -7,8 +7,8 @@ android {
|
|||||||
applicationId "com.m2049r.xmrwallet"
|
applicationId "com.m2049r.xmrwallet"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 28
|
targetSdkVersion 28
|
||||||
versionCode 170
|
versionCode 177
|
||||||
versionName "1.11.0 'Chernushka'"
|
versionName "1.11.7 'Chernushka'"
|
||||||
|
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
@@ -115,6 +115,10 @@ dependencies {
|
|||||||
implementation 'org.slf4j:slf4j-nop:1.7.25'
|
implementation 'org.slf4j:slf4j-nop:1.7.25'
|
||||||
implementation 'com.github.brnunes:swipeablerecyclerview:1.0.2'
|
implementation 'com.github.brnunes:swipeablerecyclerview:1.0.2'
|
||||||
|
|
||||||
|
// https://mvnrepository.com/artifact/com.github.aelstad/keccakj
|
||||||
|
implementation 'com.github.aelstad:keccakj:1.1.0'
|
||||||
|
|
||||||
|
|
||||||
testImplementation "junit:junit:$rootProject.ext.junitVersion"
|
testImplementation "junit:junit:$rootProject.ext.junitVersion"
|
||||||
testImplementation "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
|
testImplementation "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
|
||||||
testImplementation "com.squareup.okhttp3:mockwebserver:$rootProject.ext.okHttpVersion"
|
testImplementation "com.squareup.okhttp3:mockwebserver:$rootProject.ext.okHttpVersion"
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.m2049r.xmrwallet;
|
package com.m2049r.xmrwallet;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
@@ -33,6 +34,7 @@ import android.view.Menu;
|
|||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.WindowManager;
|
||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
@@ -45,6 +47,7 @@ import com.m2049r.xmrwallet.util.FingerprintHelper;
|
|||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
import com.m2049r.xmrwallet.util.KeyStoreHelper;
|
import com.m2049r.xmrwallet.util.KeyStoreHelper;
|
||||||
import com.m2049r.xmrwallet.util.RestoreHeight;
|
import com.m2049r.xmrwallet.util.RestoreHeight;
|
||||||
|
import com.m2049r.xmrwallet.util.ledger.Monero;
|
||||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||||
import com.nulabinc.zxcvbn.Strength;
|
import com.nulabinc.zxcvbn.Strength;
|
||||||
import com.nulabinc.zxcvbn.Zxcvbn;
|
import com.nulabinc.zxcvbn.Zxcvbn;
|
||||||
@@ -240,9 +243,7 @@ public class GenerateFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
etWalletAddress.setVisibility(View.VISIBLE);
|
etWalletAddress.setVisibility(View.VISIBLE);
|
||||||
etWalletAddress.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener()
|
etWalletAddress.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||||
|
|
||||||
{
|
|
||||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||||
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN))
|
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN))
|
||||||
|| (actionId == EditorInfo.IME_ACTION_NEXT)) {
|
|| (actionId == EditorInfo.IME_ACTION_NEXT)) {
|
||||||
@@ -274,9 +275,7 @@ public class GenerateFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
if (type.equals(TYPE_KEY)) {
|
if (type.equals(TYPE_KEY)) {
|
||||||
etWalletSpendKey.setVisibility(View.VISIBLE);
|
etWalletSpendKey.setVisibility(View.VISIBLE);
|
||||||
etWalletSpendKey.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener()
|
etWalletSpendKey.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||||
|
|
||||||
{
|
|
||||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||||
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN))
|
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN))
|
||||||
|| (actionId == EditorInfo.IME_ACTION_NEXT)) {
|
|| (actionId == EditorInfo.IME_ACTION_NEXT)) {
|
||||||
@@ -303,9 +302,7 @@ public class GenerateFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
bGenerate.setOnClickListener(new View.OnClickListener()
|
bGenerate.setOnClickListener(new View.OnClickListener() {
|
||||||
|
|
||||||
{
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
Helper.hideKeyboard(getActivity());
|
Helper.hideKeyboard(getActivity());
|
||||||
@@ -616,4 +613,80 @@ public class GenerateFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AlertDialog ledgerDialog = null;
|
||||||
|
|
||||||
|
public void convertLedgerSeed() {
|
||||||
|
if (ledgerDialog != null) return;
|
||||||
|
final Activity activity = getActivity();
|
||||||
|
View promptsView = getLayoutInflater().inflate(R.layout.prompt_ledger_seed, null);
|
||||||
|
android.app.AlertDialog.Builder alertDialogBuilder = new android.app.AlertDialog.Builder(activity);
|
||||||
|
alertDialogBuilder.setView(promptsView);
|
||||||
|
|
||||||
|
final TextInputLayout etSeed = promptsView.findViewById(R.id.etSeed);
|
||||||
|
final TextInputLayout etPassphrase = promptsView.findViewById(R.id.etPassphrase);
|
||||||
|
|
||||||
|
etSeed.getEditText().addTextChangedListener(new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
if (etSeed.getError() != null) {
|
||||||
|
etSeed.setError(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start,
|
||||||
|
int count, int after) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start,
|
||||||
|
int before, int count) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
alertDialogBuilder
|
||||||
|
.setCancelable(false)
|
||||||
|
.setPositiveButton(getString(R.string.label_ok), null)
|
||||||
|
.setNegativeButton(getString(R.string.label_cancel),
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
|
Helper.hideKeyboardAlways(activity);
|
||||||
|
etWalletMnemonic.getEditText().getText().clear();
|
||||||
|
dialog.cancel();
|
||||||
|
ledgerDialog = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ledgerDialog = alertDialogBuilder.create();
|
||||||
|
|
||||||
|
ledgerDialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
||||||
|
@Override
|
||||||
|
public void onShow(DialogInterface dialog) {
|
||||||
|
Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
|
||||||
|
button.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
String ledgerSeed = etSeed.getEditText().getText().toString();
|
||||||
|
String ledgerPassphrase = etPassphrase.getEditText().getText().toString();
|
||||||
|
String moneroSeed = Monero.convert(ledgerSeed, ledgerPassphrase);
|
||||||
|
if (moneroSeed != null) {
|
||||||
|
etWalletMnemonic.getEditText().setText(moneroSeed);
|
||||||
|
ledgerDialog.dismiss();
|
||||||
|
ledgerDialog = null;
|
||||||
|
} else {
|
||||||
|
etSeed.setError(getString(R.string.bad_ledger_seed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// set FLAG_SECURE to prevent screenshots in Release Mode
|
||||||
|
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
|
||||||
|
ledgerDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
ledgerDialog.show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -33,6 +33,7 @@ import android.view.Menu;
|
|||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.WindowManager;
|
||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
@@ -620,6 +621,11 @@ public class GenerateReviewFragment extends Fragment {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// set FLAG_SECURE to prevent screenshots in Release Mode
|
||||||
|
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
|
||||||
|
openDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||||
|
}
|
||||||
|
|
||||||
return openDialog;
|
return openDialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -929,8 +929,11 @@ public class LoginActivity extends BaseActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean createWallet(File aFile, String password) {
|
public boolean createWallet(File aFile, String password) {
|
||||||
|
NodeInfo currentNode = getNode();
|
||||||
|
final long restoreHeight =
|
||||||
|
(currentNode != null) ? currentNode.getHeight() - 20 : -1;
|
||||||
Wallet newWallet = WalletManager.getInstance()
|
Wallet newWallet = WalletManager.getInstance()
|
||||||
.createWallet(aFile, password, MNEMONIC_LANGUAGE);
|
.createWallet(aFile, password, MNEMONIC_LANGUAGE, restoreHeight);
|
||||||
boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok);
|
boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
Timber.e(newWallet.getErrorString());
|
Timber.e(newWallet.getErrorString());
|
||||||
@@ -1229,6 +1232,12 @@ public class LoginActivity extends BaseActivity
|
|||||||
case R.id.action_language:
|
case R.id.action_language:
|
||||||
onChangeLocale();
|
onChangeLocale();
|
||||||
return true;
|
return true;
|
||||||
|
case R.id.action_ledger_seed:
|
||||||
|
Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
|
||||||
|
if (f instanceof GenerateFragment) {
|
||||||
|
((GenerateFragment) f).convertLedgerSeed();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
default:
|
default:
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
@@ -1361,30 +1370,31 @@ public class LoginActivity extends BaseActivity
|
|||||||
};
|
};
|
||||||
|
|
||||||
private void connectLedger(UsbManager usbManager, final UsbDevice usbDevice) {
|
private void connectLedger(UsbManager usbManager, final UsbDevice usbDevice) {
|
||||||
try {
|
if (Ledger.ENABLED)
|
||||||
Ledger.connect(usbManager, usbDevice);
|
try {
|
||||||
registerDetachReceiver();
|
Ledger.connect(usbManager, usbDevice);
|
||||||
onLedgerAction();
|
registerDetachReceiver();
|
||||||
runOnUiThread(new Runnable() {
|
onLedgerAction();
|
||||||
@Override
|
runOnUiThread(new Runnable() {
|
||||||
public void run() {
|
@Override
|
||||||
Toast.makeText(LoginActivity.this,
|
public void run() {
|
||||||
getString(R.string.toast_ledger_attached, usbDevice.getProductName()),
|
Toast.makeText(LoginActivity.this,
|
||||||
Toast.LENGTH_SHORT)
|
getString(R.string.toast_ledger_attached, usbDevice.getProductName()),
|
||||||
.show();
|
Toast.LENGTH_SHORT)
|
||||||
}
|
.show();
|
||||||
});
|
}
|
||||||
} catch (IOException ex) {
|
});
|
||||||
runOnUiThread(new Runnable() {
|
} catch (IOException ex) {
|
||||||
@Override
|
runOnUiThread(new Runnable() {
|
||||||
public void run() {
|
@Override
|
||||||
Toast.makeText(LoginActivity.this,
|
public void run() {
|
||||||
getString(R.string.open_wallet_ledger_missing),
|
Toast.makeText(LoginActivity.this,
|
||||||
Toast.LENGTH_SHORT)
|
getString(R.string.open_wallet_ledger_missing),
|
||||||
.show();
|
Toast.LENGTH_SHORT)
|
||||||
}
|
.show();
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -440,7 +440,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
Collections.sort(nodesToTest, NodeInfo.BestNodeComparator);
|
Collections.sort(nodesToTest, NodeInfo.BestNodeComparator);
|
||||||
NodeInfo bestNode = nodesToTest.get(0);
|
NodeInfo bestNode = nodesToTest.get(0);
|
||||||
if (bestNode.isValid())
|
if (bestNode.isValid())
|
||||||
return nodesToTest.get(0);
|
return bestNode;
|
||||||
else
|
else
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@@ -32,6 +32,7 @@ import android.view.Menu;
|
|||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.WindowManager;
|
||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -505,6 +506,10 @@ public class NodeFragment extends Fragment
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// set FLAG_SECURE to prevent screenshots in Release Mode
|
||||||
|
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
|
||||||
|
editDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||||
|
}
|
||||||
|
|
||||||
etNodePass.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
etNodePass.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||||
|
@@ -302,7 +302,7 @@ public class ReceiveFragment extends Fragment {
|
|||||||
File imagePath = new File(getActivity().getCacheDir(), "images");
|
File imagePath = new File(getActivity().getCacheDir(), "images");
|
||||||
File png = new File(imagePath, "QR.png");
|
File png = new File(imagePath, "QR.png");
|
||||||
Uri contentUri = FileProvider.getUriForFile(getActivity(),
|
Uri contentUri = FileProvider.getUriForFile(getActivity(),
|
||||||
"com.m2049r.xmrwallet.fileprovider", png);
|
BuildConfig.APPLICATION_ID + ".fileprovider", png);
|
||||||
if (contentUri != null) {
|
if (contentUri != null) {
|
||||||
Intent shareIntent = new Intent();
|
Intent shareIntent = new Intent();
|
||||||
shareIntent.setAction(Intent.ACTION_SEND);
|
shareIntent.setAction(Intent.ACTION_SEND);
|
||||||
@@ -574,6 +574,7 @@ public class ReceiveFragment extends Fragment {
|
|||||||
@Override
|
@Override
|
||||||
public void onPause() {
|
public void onPause() {
|
||||||
Timber.d("onPause()");
|
Timber.d("onPause()");
|
||||||
|
Helper.hideKeyboard(getActivity());
|
||||||
super.onPause();
|
super.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -71,7 +71,6 @@ public class TxFragment extends Fragment {
|
|||||||
private TextView tvTxFee;
|
private TextView tvTxFee;
|
||||||
private TextView tvTxTransfers;
|
private TextView tvTxTransfers;
|
||||||
private TextView etTxNotes;
|
private TextView etTxNotes;
|
||||||
private Button bTxNotes;
|
|
||||||
|
|
||||||
// XMRTO stuff
|
// XMRTO stuff
|
||||||
private View cvXmrTo;
|
private View cvXmrTo;
|
||||||
@@ -102,21 +101,9 @@ public class TxFragment extends Fragment {
|
|||||||
tvTxFee = view.findViewById(R.id.tvTxFee);
|
tvTxFee = view.findViewById(R.id.tvTxFee);
|
||||||
tvTxTransfers = view.findViewById(R.id.tvTxTransfers);
|
tvTxTransfers = view.findViewById(R.id.tvTxTransfers);
|
||||||
etTxNotes = view.findViewById(R.id.etTxNotes);
|
etTxNotes = view.findViewById(R.id.etTxNotes);
|
||||||
bTxNotes = view.findViewById(R.id.bTxNotes);
|
|
||||||
|
|
||||||
etTxNotes.setRawInputType(InputType.TYPE_CLASS_TEXT);
|
etTxNotes.setRawInputType(InputType.TYPE_CLASS_TEXT);
|
||||||
|
|
||||||
bTxNotes.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
info.notes = null; // force reload on next view
|
|
||||||
bTxNotes.setEnabled(false);
|
|
||||||
etTxNotes.setEnabled(false);
|
|
||||||
userNotes.setNote(etTxNotes.getText().toString());
|
|
||||||
activityCallback.onSetNote(info.hash, userNotes.txNotes);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
tvTxXmrToKey.setOnClickListener(new View.OnClickListener() {
|
tvTxXmrToKey.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
@@ -131,14 +118,6 @@ public class TxFragment extends Fragment {
|
|||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onNotesSet(boolean reload) {
|
|
||||||
bTxNotes.setEnabled(true);
|
|
||||||
etTxNotes.setEnabled(true);
|
|
||||||
if (reload) {
|
|
||||||
loadNotes(this.info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void shareTxInfo() {
|
void shareTxInfo() {
|
||||||
if (this.info == null) return;
|
if (this.info == null) return;
|
||||||
StringBuffer sb = new StringBuffer();
|
StringBuffer sb = new StringBuffer();
|
||||||
@@ -315,7 +294,6 @@ public class TxFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -337,9 +315,9 @@ public class TxFragment extends Fragment {
|
|||||||
|
|
||||||
String getTxNotes(String hash);
|
String getTxNotes(String hash);
|
||||||
|
|
||||||
String getTxAddress(int major, int minor);
|
boolean setTxNotes(String txId, String txNotes);
|
||||||
|
|
||||||
void onSetNote(String txId, String notes);
|
String getTxAddress(int major, int minor);
|
||||||
|
|
||||||
void setToolbarButton(int type);
|
void setToolbarButton(int type);
|
||||||
|
|
||||||
@@ -357,4 +335,16 @@ public class TxFragment extends Fragment {
|
|||||||
+ " must implement Listener");
|
+ " must implement Listener");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
if (!etTxNotes.getText().toString().equals(userNotes.note)) { // notes have changed
|
||||||
|
// save them
|
||||||
|
userNotes.setNote(etTxNotes.getText().toString());
|
||||||
|
info.notes = userNotes.txNotes;
|
||||||
|
activityCallback.setTxNotes(info.hash, info.notes);
|
||||||
|
}
|
||||||
|
Helper.hideKeyboard(getActivity());
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
}
|
@@ -150,8 +150,13 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
|||||||
final WalletFragment walletFragment = (WalletFragment)
|
final WalletFragment walletFragment = (WalletFragment)
|
||||||
getSupportFragmentManager().findFragmentByTag(WalletFragment.class.getName());
|
getSupportFragmentManager().findFragmentByTag(WalletFragment.class.getName());
|
||||||
if (walletFragment != null) walletFragment.resetDismissedTransactions();
|
if (walletFragment != null) walletFragment.resetDismissedTransactions();
|
||||||
updateAccountsBalance();
|
|
||||||
forceUpdate();
|
forceUpdate();
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
updateAccountsBalance();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -174,6 +179,11 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
|||||||
return getWallet().getUserNote(txId);
|
return getWallet().getUserNote(txId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setTxNotes(String txId, String txNotes) {
|
||||||
|
return getWallet().setUserNote(txId, txNotes);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getTxAddress(int major, int minor) {
|
public String getTxAddress(int major, int minor) {
|
||||||
return getWallet().getSubaddress(major, minor);
|
return getWallet().getSubaddress(major, minor);
|
||||||
@@ -713,26 +723,6 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSetNotes(final boolean success) {
|
|
||||||
try {
|
|
||||||
final TxFragment txFragment = (TxFragment)
|
|
||||||
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
if (!success) {
|
|
||||||
Toast.makeText(WalletActivity.this, getString(R.string.tx_notes_set_failed), Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
txFragment.onNotesSet(success);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (ClassCastException ex) {
|
|
||||||
// not in tx fragment
|
|
||||||
Timber.d(ex.getLocalizedMessage());
|
|
||||||
// never mind
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onProgress(final String text) {
|
public void onProgress(final String text) {
|
||||||
try {
|
try {
|
||||||
@@ -794,21 +784,6 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSetNote(String txId, String notes) {
|
|
||||||
if (mIsBound) { // no point in talking to unbound service
|
|
||||||
Intent intent = new Intent(getApplicationContext(), WalletService.class);
|
|
||||||
intent.putExtra(WalletService.REQUEST, WalletService.REQUEST_CMD_SETNOTE);
|
|
||||||
intent.putExtra(WalletService.REQUEST_CMD_SETNOTE_TX, txId);
|
|
||||||
intent.putExtra(WalletService.REQUEST_CMD_SETNOTE_NOTES, notes);
|
|
||||||
startService(intent);
|
|
||||||
Timber.d("SET NOTE request sent");
|
|
||||||
} else {
|
|
||||||
Timber.e("Service not bound");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPrepareSend(final String tag, final TxData txData) {
|
public void onPrepareSend(final String tag, final TxData txData) {
|
||||||
if (mIsBound) { // no point in talking to unbound service
|
if (mIsBound) { // no point in talking to unbound service
|
||||||
@@ -1062,6 +1037,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
|||||||
} else {
|
} else {
|
||||||
tvBalance.setText(null);
|
tvBalance.setText(null);
|
||||||
}
|
}
|
||||||
|
updateAccountsList();
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateAccountsHeader() {
|
void updateAccountsHeader() {
|
||||||
@@ -1075,8 +1051,11 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
|||||||
Menu menu = accountsView.getMenu();
|
Menu menu = accountsView.getMenu();
|
||||||
menu.removeGroup(R.id.accounts_list);
|
menu.removeGroup(R.id.accounts_list);
|
||||||
final int n = wallet.getNumAccounts();
|
final int n = wallet.getNumAccounts();
|
||||||
|
final boolean showBalances = (n > 1) && !isStreetMode();
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
final String label = wallet.getAccountLabel(i);
|
final String label = (showBalances ?
|
||||||
|
getString(R.string.label_account, wallet.getAccountLabel(i), Helper.getDisplayAmount(wallet.getBalance(i), 2))
|
||||||
|
: wallet.getAccountLabel(i));
|
||||||
final MenuItem item = menu.add(R.id.accounts_list, getAccountId(i), 2 * i, label);
|
final MenuItem item = menu.add(R.id.accounts_list, getAccountId(i), 2 * i, label);
|
||||||
item.setIcon(R.drawable.ic_account_balance_wallet_black_24dp);
|
item.setIcon(R.drawable.ic_account_balance_wallet_black_24dp);
|
||||||
if (i == wallet.getAccountIndex())
|
if (i == wallet.getAccountIndex())
|
||||||
|
@@ -219,7 +219,7 @@ public class WalletFragment extends Fragment
|
|||||||
if (isExchanging) return; // wait for exchange to finish - it will fire this itself then.
|
if (isExchanging) return; // wait for exchange to finish - it will fire this itself then.
|
||||||
// at this point selection is XMR in case of error
|
// at this point selection is XMR in case of error
|
||||||
String displayB;
|
String displayB;
|
||||||
double amountA = Double.parseDouble(Wallet.getDisplayAmount(unlockedBalance)); // crash if this fails!
|
double amountA = Helper.getDecimalAmount(unlockedBalance).doubleValue();
|
||||||
if (!Helper.CRYPTO.equals(balanceCurrency)) { // not XMR
|
if (!Helper.CRYPTO.equals(balanceCurrency)) { // not XMR
|
||||||
double amountB = amountA * balanceRate;
|
double amountB = amountA * balanceRate;
|
||||||
displayB = Helper.getFormattedAmount(amountB, false);
|
displayB = Helper.getFormattedAmount(amountB, false);
|
||||||
@@ -235,10 +235,10 @@ public class WalletFragment extends Fragment
|
|||||||
private final ExchangeApi exchangeApi = Helper.getExchangeApi();
|
private final ExchangeApi exchangeApi = Helper.getExchangeApi();
|
||||||
|
|
||||||
void refreshBalance() {
|
void refreshBalance() {
|
||||||
double unconfirmedXmr = Double.parseDouble(Helper.getDisplayAmount(balance - unlockedBalance));
|
double unconfirmedXmr = Helper.getDecimalAmount(balance - unlockedBalance).doubleValue();
|
||||||
showUnconfirmed(unconfirmedXmr);
|
showUnconfirmed(unconfirmedXmr);
|
||||||
if (sCurrency.getSelectedItemPosition() == 0) { // XMR
|
if (sCurrency.getSelectedItemPosition() == 0) { // XMR
|
||||||
double amountXmr = Double.parseDouble(Wallet.getDisplayAmount(unlockedBalance)); // assume this cannot fail!
|
double amountXmr = Helper.getDecimalAmount(unlockedBalance).doubleValue();
|
||||||
showBalance(Helper.getFormattedAmount(amountXmr, true));
|
showBalance(Helper.getFormattedAmount(amountXmr, true));
|
||||||
} else { // not XMR
|
} else { // not XMR
|
||||||
String currency = (String) sCurrency.getSelectedItem();
|
String currency = (String) sCurrency.getSelectedItem();
|
||||||
@@ -294,7 +294,7 @@ public class WalletFragment extends Fragment
|
|||||||
|
|
||||||
public void exchangeFailed() {
|
public void exchangeFailed() {
|
||||||
sCurrency.setSelection(0, true); // default to XMR
|
sCurrency.setSelection(0, true); // default to XMR
|
||||||
double amountXmr = Double.parseDouble(Wallet.getDisplayAmount(unlockedBalance)); // assume this cannot fail!
|
double amountXmr = Helper.getDecimalAmount(unlockedBalance).doubleValue();
|
||||||
showBalance(Helper.getFormattedAmount(amountXmr, true));
|
showBalance(Helper.getFormattedAmount(amountXmr, true));
|
||||||
hideExchanging();
|
hideExchanging();
|
||||||
}
|
}
|
||||||
|
@@ -46,7 +46,8 @@ import okhttp3.ResponseBody;
|
|||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class NodeInfo extends Node {
|
public class NodeInfo extends Node {
|
||||||
final static public int MIN_MAJOR_VERSION = 9;
|
final static public int MIN_MAJOR_VERSION = 11;
|
||||||
|
final static public String RPC_VERSION = "2.0";
|
||||||
|
|
||||||
private long height = 0;
|
private long height = 0;
|
||||||
private long timestamp = 0;
|
private long timestamp = 0;
|
||||||
@@ -228,9 +229,12 @@ public class NodeInfo extends Node {
|
|||||||
responseCode = response.code();
|
responseCode = response.code();
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
ResponseBody respBody = response.body(); // closed through Response object
|
ResponseBody respBody = response.body(); // closed through Response object
|
||||||
if ((respBody != null) && (respBody.contentLength() < 1000)) { // sanity check
|
if ((respBody != null) && (respBody.contentLength() < 2000)) { // sanity check
|
||||||
final JSONObject json = new JSONObject(
|
final JSONObject json = new JSONObject(
|
||||||
respBody.string());
|
respBody.string());
|
||||||
|
String rpcVersion = json.getString("jsonrpc");
|
||||||
|
if (!RPC_VERSION.equals(rpcVersion))
|
||||||
|
return false;
|
||||||
final JSONObject header = json.getJSONObject(
|
final JSONObject header = json.getJSONObject(
|
||||||
"result").getJSONObject("block_header");
|
"result").getJSONObject("block_header");
|
||||||
height = header.getLong("height");
|
height = header.getLong("height");
|
||||||
|
@@ -62,9 +62,6 @@ public class ProgressDialog extends AlertDialog {
|
|||||||
pbBar = view.findViewById(R.id.pbBar);
|
pbBar = view.findViewById(R.id.pbBar);
|
||||||
tvProgress = view.findViewById(R.id.tvProgress);
|
tvProgress = view.findViewById(R.id.tvProgress);
|
||||||
setView(view);
|
setView(view);
|
||||||
//setTitle("blabla");
|
|
||||||
//super.setMessage("bubbu");
|
|
||||||
// view.invalidate();
|
|
||||||
setIndeterminate(indeterminate);
|
setIndeterminate(indeterminate);
|
||||||
if (maxValue > 0) {
|
if (maxValue > 0) {
|
||||||
setMax(maxValue);
|
setMax(maxValue);
|
||||||
|
@@ -33,7 +33,9 @@ import android.view.ViewGroup;
|
|||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageButton;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.R;
|
import com.m2049r.xmrwallet.R;
|
||||||
import com.m2049r.xmrwallet.data.BarcodeData;
|
import com.m2049r.xmrwallet.data.BarcodeData;
|
||||||
@@ -92,6 +94,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
private View llPaymentId;
|
private View llPaymentId;
|
||||||
private TextView tvXmrTo;
|
private TextView tvXmrTo;
|
||||||
private View llXmrTo;
|
private View llXmrTo;
|
||||||
|
private ImageButton bPasteAddress;
|
||||||
|
|
||||||
private boolean resolvingOA = false;
|
private boolean resolvingOA = false;
|
||||||
private boolean resolvingPP = false;
|
private boolean resolvingPP = false;
|
||||||
@@ -197,6 +200,21 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bPasteAddress = view.findViewById(R.id.bPasteAddress);
|
||||||
|
bPasteAddress.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
final String clip = Helper.getClipBoardText(getActivity());
|
||||||
|
if (clip == null) return;
|
||||||
|
// clean it up
|
||||||
|
final String address = clip.replaceAll("[^0-9A-Z-a-z]", "");
|
||||||
|
if (Wallet.isAddressValid(address) || BitcoinAddressValidator.validate(address))
|
||||||
|
etAddress.getEditText().setText(address);
|
||||||
|
else
|
||||||
|
Toast.makeText(getActivity(), getString(R.string.send_address_invalid), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
etPaymentId = view.findViewById(R.id.etPaymentId);
|
etPaymentId = view.findViewById(R.id.etPaymentId);
|
||||||
etPaymentId.getEditText().setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
etPaymentId.getEditText().setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||||
etPaymentId.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
etPaymentId.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||||
|
@@ -27,11 +27,13 @@ import android.view.KeyEvent;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.WindowManager;
|
||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.BuildConfig;
|
||||||
import com.m2049r.xmrwallet.R;
|
import com.m2049r.xmrwallet.R;
|
||||||
import com.m2049r.xmrwallet.data.TxData;
|
import com.m2049r.xmrwallet.data.TxData;
|
||||||
import com.m2049r.xmrwallet.data.TxDataBtc;
|
import com.m2049r.xmrwallet.data.TxDataBtc;
|
||||||
@@ -435,6 +437,11 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// set FLAG_SECURE to prevent screenshots in Release Mode
|
||||||
|
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
|
||||||
|
passwordDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||||
|
}
|
||||||
|
|
||||||
passwordDialog.show();
|
passwordDialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -27,10 +27,12 @@ import android.view.KeyEvent;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.WindowManager;
|
||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.BuildConfig;
|
||||||
import com.m2049r.xmrwallet.R;
|
import com.m2049r.xmrwallet.R;
|
||||||
import com.m2049r.xmrwallet.data.TxData;
|
import com.m2049r.xmrwallet.data.TxData;
|
||||||
import com.m2049r.xmrwallet.model.PendingTransaction;
|
import com.m2049r.xmrwallet.model.PendingTransaction;
|
||||||
@@ -322,6 +324,11 @@ public class SendConfirmWizardFragment extends SendWizardFragment implements Sen
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// set FLAG_SECURE to prevent screenshots in Release Mode
|
||||||
|
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
|
||||||
|
passwordDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||||
|
}
|
||||||
|
|
||||||
passwordDialog.show();
|
passwordDialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -30,6 +30,7 @@ import com.m2049r.xmrwallet.model.TransactionInfo;
|
|||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
import com.m2049r.xmrwallet.data.UserNotes;
|
import com.m2049r.xmrwallet.data.UserNotes;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
@@ -154,7 +155,7 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((infoItem.fee > 0)) {
|
if ((infoItem.fee > 0)) {
|
||||||
String fee = Helper.getDisplayAmount(infoItem.fee, 5);
|
String fee = Helper.getDisplayAmount(infoItem.fee, Helper.DISPLAY_DIGITS_INFO);
|
||||||
tvFee.setText(context.getString(R.string.tx_list_fee, fee));
|
tvFee.setText(context.getString(R.string.tx_list_fee, fee));
|
||||||
tvFee.setVisibility(View.VISIBLE);
|
tvFee.setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -34,6 +34,7 @@ import java.io.IOException;
|
|||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class Ledger {
|
public class Ledger {
|
||||||
|
static final public boolean ENABLED = true;
|
||||||
// 5:20 is same as wallet2.cpp::restore()
|
// 5:20 is same as wallet2.cpp::restore()
|
||||||
static public final int LOOKAHEAD_ACCOUNTS = 5;
|
static public final int LOOKAHEAD_ACCOUNTS = 5;
|
||||||
static public final int LOOKAHEAD_SUBADDRESSES = 20;
|
static public final int LOOKAHEAD_SUBADDRESSES = 20;
|
||||||
@@ -44,6 +45,7 @@ public class Ledger {
|
|||||||
public static final int OK[] = {SW_OK};
|
public static final int OK[] = {SW_OK};
|
||||||
|
|
||||||
public static UsbDevice findDevice(UsbManager usbManager) {
|
public static UsbDevice findDevice(UsbManager usbManager) {
|
||||||
|
if (!ENABLED) return null;
|
||||||
return BTChipTransportAndroidHID.getDevice(usbManager);
|
return BTChipTransportAndroidHID.getDevice(usbManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -173,5 +173,4 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
|
|||||||
return this.hash.compareTo(another.hash);
|
return this.hash.compareTo(another.hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
@@ -353,9 +353,10 @@ public class Wallet {
|
|||||||
if (label.equals(NEW_ACCOUNT_NAME)) {
|
if (label.equals(NEW_ACCOUNT_NAME)) {
|
||||||
String address = getAddress(accountIndex);
|
String address = getAddress(accountIndex);
|
||||||
int len = address.length();
|
int len = address.length();
|
||||||
return address.substring(0, 6) +
|
label = address.substring(0, 6) +
|
||||||
"\u2026" + address.substring(len - 6, len);
|
"\u2026" + address.substring(len - 6, len);
|
||||||
} else return label;
|
}
|
||||||
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSubaddressLabel(int addressIndex) {
|
public String getSubaddressLabel(int addressIndex) {
|
||||||
|
@@ -91,14 +91,16 @@ public class WalletManager {
|
|||||||
managedWallet = null;
|
managedWallet = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Wallet createWallet(File aFile, String password, String language) {
|
public Wallet createWallet(File aFile, String password, String language, long height) {
|
||||||
long walletHandle = createWalletJ(aFile.getAbsolutePath(), password, language, getNetworkType().getValue());
|
long walletHandle = createWalletJ(aFile.getAbsolutePath(), password, language, getNetworkType().getValue());
|
||||||
Wallet wallet = new Wallet(walletHandle);
|
Wallet wallet = new Wallet(walletHandle);
|
||||||
manageWallet(wallet);
|
manageWallet(wallet);
|
||||||
if (wallet.getStatus() == Wallet.Status.Status_Ok) {
|
if (wallet.getStatus() == Wallet.Status.Status_Ok) {
|
||||||
// (Re-)Estimate restore height based on what we know
|
// (Re-)Estimate restore height based on what we know
|
||||||
long oldHeight = wallet.getRestoreHeight();
|
final long oldHeight = wallet.getRestoreHeight();
|
||||||
wallet.setRestoreHeight(RestoreHeight.getInstance().getHeight(new Date()));
|
final long restoreHeight =
|
||||||
|
(height > -1) ? height : RestoreHeight.getInstance().getHeight(new Date());
|
||||||
|
wallet.setRestoreHeight(restoreHeight);
|
||||||
Timber.d("Changed Restore Height from %d to %d", oldHeight, wallet.getRestoreHeight());
|
Timber.d("Changed Restore Height from %d to %d", oldHeight, wallet.getRestoreHeight());
|
||||||
wallet.setPassword(password); // this rewrites the keys file (which contains the restore height)
|
wallet.setPassword(password); // this rewrites the keys file (which contains the restore height)
|
||||||
}
|
}
|
||||||
|
@@ -69,10 +69,6 @@ public class WalletService extends Service {
|
|||||||
public static final String REQUEST_CMD_SEND = "send";
|
public static final String REQUEST_CMD_SEND = "send";
|
||||||
public static final String REQUEST_CMD_SEND_NOTES = "notes";
|
public static final String REQUEST_CMD_SEND_NOTES = "notes";
|
||||||
|
|
||||||
public static final String REQUEST_CMD_SETNOTE = "setnote";
|
|
||||||
public static final String REQUEST_CMD_SETNOTE_TX = "tx";
|
|
||||||
public static final String REQUEST_CMD_SETNOTE_NOTES = "notes";
|
|
||||||
|
|
||||||
public static final int START_SERVICE = 1;
|
public static final int START_SERVICE = 1;
|
||||||
public static final int STOP_SERVICE = 2;
|
public static final int STOP_SERVICE = 2;
|
||||||
|
|
||||||
@@ -224,8 +220,6 @@ public class WalletService extends Service {
|
|||||||
|
|
||||||
void onSendTransactionFailed(String error);
|
void onSendTransactionFailed(String error);
|
||||||
|
|
||||||
void onSetNotes(boolean success);
|
|
||||||
|
|
||||||
void onWalletStarted(Wallet.ConnectionStatus walletStatus);
|
void onWalletStarted(Wallet.ConnectionStatus walletStatus);
|
||||||
|
|
||||||
void onWalletOpen(Wallet.Device device);
|
void onWalletOpen(Wallet.Device device);
|
||||||
@@ -378,26 +372,6 @@ public class WalletService extends Service {
|
|||||||
if (observer != null) observer.onSendTransactionFailed(error);
|
if (observer != null) observer.onSendTransactionFailed(error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (cmd.equals(REQUEST_CMD_SETNOTE)) {
|
|
||||||
Wallet myWallet = getWallet();
|
|
||||||
Timber.d("SET NOTE for wallet: %s", myWallet.getName());
|
|
||||||
String txId = extras.getString(REQUEST_CMD_SETNOTE_TX);
|
|
||||||
String notes = extras.getString(REQUEST_CMD_SETNOTE_NOTES);
|
|
||||||
if ((txId != null) && (notes != null)) {
|
|
||||||
boolean success = myWallet.setUserNote(txId, notes);
|
|
||||||
if (!success) {
|
|
||||||
Timber.e(myWallet.getErrorString());
|
|
||||||
}
|
|
||||||
if (observer != null) observer.onSetNotes(success);
|
|
||||||
if (success) {
|
|
||||||
boolean rc = myWallet.store();
|
|
||||||
Timber.d("wallet stored: %s with rc=%b", myWallet.getName(), rc);
|
|
||||||
if (!rc) {
|
|
||||||
Timber.w("Wallet store failed: %s", myWallet.getErrorString());
|
|
||||||
}
|
|
||||||
if (observer != null) observer.onWalletStored(rc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@@ -21,6 +21,7 @@ import android.app.Activity;
|
|||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipDescription;
|
||||||
import android.content.ClipboardManager;
|
import android.content.ClipboardManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
@@ -64,6 +65,7 @@ import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
@@ -186,28 +188,23 @@ public class Helper {
|
|||||||
act.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
act.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static public BigDecimal getDecimalAmount(long amount) {
|
||||||
|
return new BigDecimal(amount).scaleByPowerOfTen(-12);
|
||||||
|
}
|
||||||
|
|
||||||
static public String getDisplayAmount(long amount) {
|
static public String getDisplayAmount(long amount) {
|
||||||
return getDisplayAmount(amount, 12);
|
return getDisplayAmount(amount, 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
static public String getDisplayAmount(long amount, int maxDecimals) {
|
static public String getDisplayAmount(long amount, int maxDecimals) {
|
||||||
return getDisplayAmount(Wallet.getDisplayAmount(amount), maxDecimals);
|
// a Java bug does not strip zeros properly if the value is 0
|
||||||
}
|
if (amount == 0) return "0.00";
|
||||||
|
BigDecimal d = getDecimalAmount(amount)
|
||||||
// amountString must have '.' as decimal point
|
.setScale(maxDecimals, BigDecimal.ROUND_HALF_UP)
|
||||||
private static String getDisplayAmount(String amountString, int maxDecimals) {
|
.stripTrailingZeros();
|
||||||
int lastZero = 0;
|
if (d.scale() < 2)
|
||||||
int decimal = 0;
|
d = d.setScale(2, BigDecimal.ROUND_UNNECESSARY);
|
||||||
for (int i = amountString.length() - 1; i >= 0; i--) {
|
return d.toPlainString();
|
||||||
if ((lastZero == 0) && (amountString.charAt(i) != '0')) lastZero = i + 1;
|
|
||||||
// TODO i18n
|
|
||||||
if (amountString.charAt(i) == '.') {
|
|
||||||
decimal = i + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int cutoff = Math.min(Math.max(lastZero, decimal + 2), decimal + maxDecimals);
|
|
||||||
return amountString.substring(0, cutoff);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static public String getFormattedAmount(double amount, boolean isXmr) {
|
static public String getFormattedAmount(double amount, boolean isXmr) {
|
||||||
@@ -285,6 +282,16 @@ public class Helper {
|
|||||||
clipboardManager.setPrimaryClip(clip);
|
clipboardManager.setPrimaryClip(clip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static public String getClipBoardText(Context context) {
|
||||||
|
final ClipboardManager clipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
if (clipboardManager.hasPrimaryClip()
|
||||||
|
&& clipboardManager.getPrimaryClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {
|
||||||
|
final ClipData.Item item = clipboardManager.getPrimaryClip().getItemAt(0);
|
||||||
|
return item.getText().toString();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
static private Animation ShakeAnimation;
|
static private Animation ShakeAnimation;
|
||||||
|
|
||||||
static public Animation getShakeAnimation(Context context) {
|
static public Animation getShakeAnimation(Context context) {
|
||||||
@@ -556,6 +563,8 @@ public class Helper {
|
|||||||
tvOpenPrompt.setText(context.getText(R.string.prompt_fingerprint_auth));
|
tvOpenPrompt.setText(context.getText(R.string.prompt_fingerprint_auth));
|
||||||
tvOpenPrompt.setVisibility(View.VISIBLE);
|
tvOpenPrompt.setVisibility(View.VISIBLE);
|
||||||
FingerprintHelper.authenticate(context, cancelSignal, fingerprintAuthCallback);
|
FingerprintHelper.authenticate(context, cancelSignal, fingerprintAuthCallback);
|
||||||
|
} else {
|
||||||
|
etPassword.requestFocus();
|
||||||
}
|
}
|
||||||
Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
|
Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
|
||||||
button.setOnClickListener(new View.OnClickListener() {
|
button.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@@ -101,6 +101,8 @@ public class RestoreHeight {
|
|||||||
blockheight.put("2019-01-01", 1738923L);
|
blockheight.put("2019-01-01", 1738923L);
|
||||||
blockheight.put("2019-02-01", 1761435L);
|
blockheight.put("2019-02-01", 1761435L);
|
||||||
blockheight.put("2019-03-01", 1781681L);
|
blockheight.put("2019-03-01", 1781681L);
|
||||||
|
blockheight.put("2019-04-01", 1803081L);
|
||||||
|
blockheight.put("2019-05-01", 1824671L);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getHeight(String date) {
|
public long getHeight(String date) {
|
||||||
|
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Based on
|
||||||
|
* https://stackoverflow.com/a/19943894
|
||||||
|
*
|
||||||
|
* Curve parameters from
|
||||||
|
* https://en.bitcoin.it/wiki/Secp256k1
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 m2049r
|
||||||
|
* Copyright (c) 2013 ChiaraHsieh
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.m2049r.xmrwallet.util.ledger;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.spec.ECPoint;
|
||||||
|
|
||||||
|
public class ECsecp256k1 {
|
||||||
|
static private final BigInteger TWO = new BigInteger("2");
|
||||||
|
static private final BigInteger THREE = new BigInteger("3");
|
||||||
|
static public final BigInteger p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16);
|
||||||
|
static public final BigInteger a = new BigInteger("0000000000000000000000000000000000000000000000000000000000000000", 16);
|
||||||
|
static public final BigInteger b = new BigInteger("0000000000000000000000000000000000000000000000000000000000000007", 16);
|
||||||
|
static public final BigInteger n = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16);
|
||||||
|
static public final ECPoint G = new ECPoint(
|
||||||
|
new BigInteger("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16),
|
||||||
|
new BigInteger("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16));
|
||||||
|
|
||||||
|
public static ECPoint scalmult(BigInteger kin, ECPoint P) {
|
||||||
|
ECPoint R = ECPoint.POINT_INFINITY, S = P;
|
||||||
|
BigInteger k = kin.mod(n); // not necessary b/c that's how curves work
|
||||||
|
int length = k.bitLength();
|
||||||
|
byte[] binarray = new byte[length];
|
||||||
|
for (int i = 0; i <= length - 1; i++) {
|
||||||
|
binarray[i] = k.mod(TWO).byteValue();
|
||||||
|
k = k.divide(TWO);
|
||||||
|
}
|
||||||
|
for (int i = length - 1; i >= 0; i--) {
|
||||||
|
// i should start at length-1 not -2 because the MSB of binary may not be 1
|
||||||
|
R = doublePoint(R);
|
||||||
|
if (binarray[i] == 1)
|
||||||
|
R = addPoint(R, S);
|
||||||
|
}
|
||||||
|
return R;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ECPoint addPoint(ECPoint r, ECPoint s) {
|
||||||
|
if (r.equals(s))
|
||||||
|
return doublePoint(r);
|
||||||
|
else if (r.equals(ECPoint.POINT_INFINITY))
|
||||||
|
return s;
|
||||||
|
else if (s.equals(ECPoint.POINT_INFINITY))
|
||||||
|
return r;
|
||||||
|
BigInteger slope = (r.getAffineY().subtract(s.getAffineY()))
|
||||||
|
.multiply(r.getAffineX().subtract(s.getAffineX()).modInverse(p));
|
||||||
|
BigInteger Xout = (slope.modPow(TWO, p).subtract(r.getAffineX())).subtract(s.getAffineX()).mod(p);
|
||||||
|
BigInteger Yout = s.getAffineY().negate().add(slope.multiply(s.getAffineX().subtract(Xout))).mod(p);
|
||||||
|
return new ECPoint(Xout, Yout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ECPoint doublePoint(ECPoint r) {
|
||||||
|
if (r.equals(ECPoint.POINT_INFINITY))
|
||||||
|
return r;
|
||||||
|
BigInteger slope = (r.getAffineX().pow(2)).multiply(THREE).add(a)
|
||||||
|
.multiply((r.getAffineY().multiply(TWO)).modInverse(p));
|
||||||
|
BigInteger Xout = slope.pow(2).subtract(r.getAffineX().multiply(TWO)).mod(p);
|
||||||
|
BigInteger Yout = (r.getAffineY().negate()).add(slope.multiply(r.getAffineX().subtract(Xout))).mod(p);
|
||||||
|
return new ECPoint(Xout, Yout);
|
||||||
|
}
|
||||||
|
}
|
1866
app/src/main/java/com/m2049r/xmrwallet/util/ledger/Monero.java
Normal file
1866
app/src/main/java/com/m2049r/xmrwallet/util/ledger/Monero.java
Normal file
File diff suppressed because it is too large
Load Diff
170
app/src/main/java/com/theromus/sha/Keccak.java
Normal file
170
app/src/main/java/com/theromus/sha/Keccak.java
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
package com.theromus.sha;
|
||||||
|
|
||||||
|
import static com.theromus.utils.HexUtils.leftRotate64;
|
||||||
|
import static com.theromus.utils.HexUtils.convertToUint;
|
||||||
|
import static com.theromus.utils.HexUtils.convertFromLittleEndianTo64;
|
||||||
|
import static com.theromus.utils.HexUtils.convertFrom64ToLittleEndian;
|
||||||
|
import static java.lang.Math.min;
|
||||||
|
import static java.lang.System.arraycopy;
|
||||||
|
import static java.util.Arrays.fill;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keccak implementation.
|
||||||
|
*
|
||||||
|
* @author romus
|
||||||
|
*/
|
||||||
|
public class Keccak {
|
||||||
|
|
||||||
|
private static BigInteger BIT_64 = new BigInteger("18446744073709551615");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do hash.
|
||||||
|
*
|
||||||
|
* @param message input data
|
||||||
|
* @param parameter keccak param
|
||||||
|
* @return byte-array result
|
||||||
|
*/
|
||||||
|
public byte[] getHash(final byte[] message, final Parameters parameter) {
|
||||||
|
int[] uState = new int[200];
|
||||||
|
int[] uMessage = convertToUint(message);
|
||||||
|
|
||||||
|
|
||||||
|
int rateInBytes = parameter.getRate() / 8;
|
||||||
|
int blockSize = 0;
|
||||||
|
int inputOffset = 0;
|
||||||
|
|
||||||
|
// Absorbing phase
|
||||||
|
while (inputOffset < uMessage.length) {
|
||||||
|
blockSize = min(uMessage.length - inputOffset, rateInBytes);
|
||||||
|
for (int i = 0; i < blockSize; i++) {
|
||||||
|
uState[i] = uState[i] ^ uMessage[i + inputOffset];
|
||||||
|
}
|
||||||
|
|
||||||
|
inputOffset = inputOffset + blockSize;
|
||||||
|
if (blockSize == rateInBytes) {
|
||||||
|
doKeccakf(uState);
|
||||||
|
blockSize = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Padding phase
|
||||||
|
uState[blockSize] = uState[blockSize] ^ parameter.getD();
|
||||||
|
if ((parameter.getD() & 0x80) != 0 && blockSize == (rateInBytes - 1)) {
|
||||||
|
doKeccakf(uState);
|
||||||
|
}
|
||||||
|
|
||||||
|
uState[rateInBytes - 1] = uState[rateInBytes - 1] ^ 0x80;
|
||||||
|
doKeccakf(uState);
|
||||||
|
|
||||||
|
// Squeezing phase
|
||||||
|
ByteArrayOutputStream byteResults = new ByteArrayOutputStream();
|
||||||
|
int tOutputLen = parameter.getOutputLen() / 8;
|
||||||
|
while (tOutputLen > 0) {
|
||||||
|
blockSize = min(tOutputLen, rateInBytes);
|
||||||
|
for (int i = 0; i < blockSize; i++) {
|
||||||
|
byteResults.write((byte) uState[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
tOutputLen -= blockSize;
|
||||||
|
if (tOutputLen > 0) {
|
||||||
|
doKeccakf(uState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return byteResults.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doKeccakf(final int[] uState) {
|
||||||
|
BigInteger[][] lState = new BigInteger[5][5];
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
for (int j = 0; j < 5; j++) {
|
||||||
|
int[] data = new int[8];
|
||||||
|
arraycopy(uState, 8 * (i + 5 * j), data, 0, data.length);
|
||||||
|
lState[i][j] = convertFromLittleEndianTo64(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
roundB(lState);
|
||||||
|
|
||||||
|
fill(uState, 0);
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
for (int j = 0; j < 5; j++) {
|
||||||
|
int[] data = convertFrom64ToLittleEndian(lState[i][j]);
|
||||||
|
arraycopy(data, 0, uState, 8 * (i + 5 * j), data.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permutation on the given state.
|
||||||
|
*
|
||||||
|
* @param state state
|
||||||
|
*/
|
||||||
|
private void roundB(final BigInteger[][] state) {
|
||||||
|
int LFSRstate = 1;
|
||||||
|
for (int round = 0; round < 24; round++) {
|
||||||
|
BigInteger[] C = new BigInteger[5];
|
||||||
|
BigInteger[] D = new BigInteger[5];
|
||||||
|
|
||||||
|
// θ step
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
C[i] = state[i][0].xor(state[i][1]).xor(state[i][2]).xor(state[i][3]).xor(state[i][4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
D[i] = C[(i + 4) % 5].xor(leftRotate64(C[(i + 1) % 5], 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
for (int j = 0; j < 5; j++) {
|
||||||
|
state[i][j] = state[i][j].xor(D[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//ρ and π steps
|
||||||
|
int x = 1, y = 0;
|
||||||
|
BigInteger current = state[x][y];
|
||||||
|
for (int i = 0; i < 24; i++) {
|
||||||
|
int tX = x;
|
||||||
|
x = y;
|
||||||
|
y = (2 * tX + 3 * y) % 5;
|
||||||
|
|
||||||
|
BigInteger shiftValue = current;
|
||||||
|
current = state[x][y];
|
||||||
|
|
||||||
|
state[x][y] = leftRotate64(shiftValue, (i + 1) * (i + 2) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
//χ step
|
||||||
|
for (int j = 0; j < 5; j++) {
|
||||||
|
BigInteger[] t = new BigInteger[5];
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
t[i] = state[i][j];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
// ~t[(i + 1) % 5]
|
||||||
|
BigInteger invertVal = t[(i + 1) % 5].xor(BIT_64);
|
||||||
|
// t[i] ^ ((~t[(i + 1) % 5]) & t[(i + 2) % 5])
|
||||||
|
state[i][j] = t[i].xor(invertVal.and(t[(i + 2) % 5]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//ι step
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
LFSRstate = ((LFSRstate << 1) ^ ((LFSRstate >> 7) * 0x71)) % 256;
|
||||||
|
// pow(2, i) - 1
|
||||||
|
int bitPosition = (1 << i) - 1;
|
||||||
|
if ((LFSRstate & 2) != 0) {
|
||||||
|
state[0][0] = state[0][0].xor(new BigInteger("1").shiftLeft(bitPosition));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
51
app/src/main/java/com/theromus/sha/Parameters.java
Normal file
51
app/src/main/java/com/theromus/sha/Parameters.java
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package com.theromus.sha;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The parameters defining the standard FIPS 202.
|
||||||
|
*
|
||||||
|
* @author romus
|
||||||
|
*/
|
||||||
|
public enum Parameters {
|
||||||
|
KECCAK_224 (1152, 0x01, 224),
|
||||||
|
KECCAK_256 (1088, 0x01, 256),
|
||||||
|
KECCAK_384 (832, 0x01, 384),
|
||||||
|
KECCAK_512 (576, 0x01, 512),
|
||||||
|
|
||||||
|
SHA3_224 (1152, 0x06, 224),
|
||||||
|
SHA3_256 (1088, 0x06, 256),
|
||||||
|
SHA3_384 (832, 0x06, 384),
|
||||||
|
SHA3_512 (576, 0x06, 512),
|
||||||
|
|
||||||
|
SHAKE128 (1344, 0x1F, 256),
|
||||||
|
SHAKE256 (1088, 0x1F, 512);
|
||||||
|
|
||||||
|
private final int rate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delimited suffix.
|
||||||
|
*/
|
||||||
|
public final int d;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output length (bits).
|
||||||
|
*/
|
||||||
|
public final int outputLen;
|
||||||
|
|
||||||
|
Parameters(int rate, int d, int outputLen) {
|
||||||
|
this.rate = rate;
|
||||||
|
this.d = d;
|
||||||
|
this.outputLen = outputLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRate() {
|
||||||
|
return rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getD() {
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOutputLen() {
|
||||||
|
return outputLen;
|
||||||
|
}
|
||||||
|
}
|
97
app/src/main/java/com/theromus/utils/HexUtils.java
Normal file
97
app/src/main/java/com/theromus/utils/HexUtils.java
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package com.theromus.utils;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hex-utils.
|
||||||
|
*
|
||||||
|
* @author romus
|
||||||
|
*/
|
||||||
|
public class HexUtils {
|
||||||
|
|
||||||
|
private static final byte[] ENCODE_BYTE_TABLE = {
|
||||||
|
(byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
|
||||||
|
(byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert byte array to unsigned array.
|
||||||
|
*
|
||||||
|
* @param data byte array
|
||||||
|
* @return unsigned array
|
||||||
|
*/
|
||||||
|
public static int[] convertToUint(final byte[] data) {
|
||||||
|
int[] converted = new int[data.length];
|
||||||
|
for (int i = 0; i < data.length; i++) {
|
||||||
|
converted[i] = data[i] & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
return converted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert LE to 64-bit value (unsigned long).
|
||||||
|
*
|
||||||
|
* @param data data
|
||||||
|
* @return 64-bit value (unsigned long)
|
||||||
|
*/
|
||||||
|
public static BigInteger convertFromLittleEndianTo64(final int[] data) {
|
||||||
|
BigInteger uLong = new BigInteger("0");
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
uLong = uLong.add(new BigInteger(Integer.toString(data[i])).shiftLeft(8 * i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return uLong;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert 64-bit (unsigned long) value to LE.
|
||||||
|
*
|
||||||
|
* @param uLong 64-bit value (unsigned long)
|
||||||
|
* @return LE
|
||||||
|
*/
|
||||||
|
public static int[] convertFrom64ToLittleEndian(final BigInteger uLong) {
|
||||||
|
int[] data = new int[8];
|
||||||
|
BigInteger mod256 = new BigInteger("256");
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
data[i] = uLong.shiftRight((8 * i)).mod(mod256).intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bitwise rotate left.
|
||||||
|
*
|
||||||
|
* @param value unsigned long value
|
||||||
|
* @param rotate rotate left
|
||||||
|
* @return result
|
||||||
|
*/
|
||||||
|
public static BigInteger leftRotate64(final BigInteger value, final int rotate) {
|
||||||
|
BigInteger lp = value.shiftRight(64 - (rotate % 64));
|
||||||
|
BigInteger rp = value.shiftLeft(rotate % 64);
|
||||||
|
|
||||||
|
return lp.add(rp).mod(new BigInteger("18446744073709551616"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert bytes to string.
|
||||||
|
*
|
||||||
|
* @param data bytes array
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static String convertBytesToString(final byte[] data) {
|
||||||
|
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||||
|
for (int i = 0; i < data.length; i++) {
|
||||||
|
int uVal = data[i] & 0xFF;
|
||||||
|
|
||||||
|
buffer.write(ENCODE_BYTE_TABLE[(uVal >>> 4)]);
|
||||||
|
buffer.write(ENCODE_BYTE_TABLE[uVal & 0xF]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new String(buffer.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/gradientOrange"
|
||||||
|
android:pathData="M19,2h-4.18C14.4,0.84 13.3,0 12,0c-1.3,0 -2.4,0.84 -2.82,2L5,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,4c0,-1.1 -0.9,-2 -2,-2zM12,2c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM19,20L5,20L5,4h2v3h10L17,4h2v16z" />
|
||||||
|
</vector>
|
@@ -37,8 +37,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_toStartOf="@+id/ibOption"
|
android:layout_toStartOf="@+id/ibOption">
|
||||||
android:layout_toEndOf="@id/ibBookmark">
|
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/pbNode"
|
android:id="@+id/pbNode"
|
||||||
|
@@ -20,21 +20,46 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp" />
|
android:layout_height="0dp" />
|
||||||
|
|
||||||
<android.support.design.widget.TextInputLayout
|
<RelativeLayout
|
||||||
android:id="@+id/etAddress"
|
android:id="@+id/llAddress"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:errorEnabled="true">
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<android.support.design.widget.TextInputEditText
|
<android.support.design.widget.TextInputLayout
|
||||||
style="@style/MoneroEdit"
|
android:id="@+id/etAddress"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="@string/send_address_hint"
|
android:layout_alignParentStart="true"
|
||||||
android:imeOptions="actionNext"
|
android:layout_toStartOf="@+id/bPasteAddress"
|
||||||
android:inputType="textMultiLine"
|
app:counterEnabled="true"
|
||||||
android:textAlignment="textStart" />
|
app:counterMaxLength="16"
|
||||||
</android.support.design.widget.TextInputLayout>
|
app:errorEnabled="true">
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputEditText
|
||||||
|
style="@style/MoneroEdit"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="10"
|
||||||
|
android:hint="@string/send_address_hint"
|
||||||
|
android:imeOptions="actionNext"
|
||||||
|
android:inputType="textMultiLine"
|
||||||
|
android:textAlignment="textStart" />
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/bPasteAddress"
|
||||||
|
style="@style/MoneroText.Button.Small"
|
||||||
|
android:layout_width="56dp"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:background="?android:selectableItemBackgroundBorderless"
|
||||||
|
android:src="@drawable/ic_content_paste_orange_24dp" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@@ -225,34 +225,14 @@
|
|||||||
android:padding="8sp"
|
android:padding="8sp"
|
||||||
android:text="@string/tx_notes" />
|
android:text="@string/tx_notes" />
|
||||||
|
|
||||||
<RelativeLayout
|
<EditText
|
||||||
|
android:id="@+id/etTxNotes"
|
||||||
|
style="@style/MoneroEdit"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:backgroundTint="@color/moneroGray"
|
||||||
<EditText
|
android:hint="@string/tx_notes_hint"
|
||||||
android:id="@+id/etTxNotes"
|
android:inputType="textMultiLine" />
|
||||||
style="@style/MoneroEdit"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentStart="true"
|
|
||||||
android:layout_toStartOf="@+id/bTxNotes"
|
|
||||||
android:backgroundTint="@color/moneroGray"
|
|
||||||
android:hint="@string/tx_notes_hint"
|
|
||||||
android:inputType="textMultiLine" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/bTxNotes"
|
|
||||||
style="@style/MoneroButton.Small"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_centerInParent="true"
|
|
||||||
android:enabled="true"
|
|
||||||
android:paddingStart="8dp"
|
|
||||||
android:paddingEnd="8dp"
|
|
||||||
android:text="@string/tx_button_notes" />
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user