You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
460 lines
16 KiB
460 lines
16 KiB
/*
|
|
* Copyright (c) 2017 m2049r
|
|
*
|
|
* 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;
|
|
|
|
import android.content.Context;
|
|
import android.content.DialogInterface;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.support.v4.app.Fragment;
|
|
import android.support.v7.app.AlertDialog;
|
|
import android.text.Editable;
|
|
import android.text.InputType;
|
|
import android.text.TextWatcher;
|
|
import android.util.Log;
|
|
import android.view.KeyEvent;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.view.inputmethod.EditorInfo;
|
|
import android.widget.Button;
|
|
import android.widget.EditText;
|
|
import android.widget.LinearLayout;
|
|
import android.widget.ProgressBar;
|
|
import android.widget.Spinner;
|
|
import android.widget.TextView;
|
|
|
|
import com.m2049r.xmrwallet.model.PendingTransaction;
|
|
import com.m2049r.xmrwallet.model.Wallet;
|
|
import com.m2049r.xmrwallet.model.WalletManager;
|
|
import com.m2049r.xmrwallet.util.Helper;
|
|
import com.m2049r.xmrwallet.util.BarcodeData;
|
|
import com.m2049r.xmrwallet.util.TxData;
|
|
|
|
public class SendFragment extends Fragment {
|
|
static final String TAG = "SendFragment";
|
|
|
|
EditText etAddress;
|
|
EditText etPaymentId;
|
|
EditText etAmount;
|
|
Button bScan;
|
|
Button bSweep;
|
|
Spinner sMixin;
|
|
Spinner sPriority;
|
|
Button bPrepareSend;
|
|
Button bDispose;
|
|
Button bPaymentId;
|
|
LinearLayout llConfirmSend;
|
|
TextView tvTxAmount;
|
|
TextView tvTxFee;
|
|
TextView tvTxDust;
|
|
EditText etNotes;
|
|
Button bSend;
|
|
Button bReallySend;
|
|
ProgressBar pbProgress;
|
|
|
|
final static int Mixins[] = {4, 7, 12, 25}; // must match the layout XML
|
|
final static PendingTransaction.Priority Priorities[] =
|
|
{PendingTransaction.Priority.Priority_Default,
|
|
PendingTransaction.Priority.Priority_Low,
|
|
PendingTransaction.Priority.Priority_Medium,
|
|
PendingTransaction.Priority.Priority_High}; // must match the layout XML
|
|
|
|
@Override
|
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
|
Bundle savedInstanceState) {
|
|
|
|
View view = inflater.inflate(R.layout.send_fragment, container, false);
|
|
|
|
sMixin = (Spinner) view.findViewById(R.id.sMixin);
|
|
sPriority = (Spinner) view.findViewById(R.id.sPriority);
|
|
etAddress = (EditText) view.findViewById(R.id.etAddress);
|
|
etPaymentId = (EditText) view.findViewById(R.id.etPaymentId);
|
|
etAmount = (EditText) view.findViewById(R.id.etAmount);
|
|
bScan = (Button) view.findViewById(R.id.bScan);
|
|
bSweep = (Button) view.findViewById(R.id.bSweep);
|
|
bPrepareSend = (Button) view.findViewById(R.id.bPrepareSend);
|
|
bPaymentId = (Button) view.findViewById(R.id.bPaymentId);
|
|
bDispose = (Button) view.findViewById(R.id.bDispose);
|
|
|
|
llConfirmSend = (LinearLayout) view.findViewById(R.id.llConfirmSend);
|
|
tvTxAmount = (TextView) view.findViewById(R.id.tvTxAmount);
|
|
tvTxFee = (TextView) view.findViewById(R.id.tvTxFee);
|
|
tvTxDust = (TextView) view.findViewById(R.id.tvTxDust);
|
|
etNotes = (EditText) view.findViewById(R.id.etNotes);
|
|
bSend = (Button) view.findViewById(R.id.bSend);
|
|
bReallySend = (Button) view.findViewById(R.id.bReallySend);
|
|
|
|
pbProgress = (ProgressBar) view.findViewById(R.id.pbProgress);
|
|
|
|
etAddress.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
|
etPaymentId.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
|
etNotes.setRawInputType(InputType.TYPE_CLASS_TEXT);
|
|
|
|
Helper.showKeyboard(getActivity());
|
|
etAddress.requestFocus();
|
|
etAddress.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
|
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
|
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) {
|
|
if (addressOk()) {
|
|
etPaymentId.requestFocus();
|
|
} // otherwise ignore
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
etAddress.addTextChangedListener(new TextWatcher() {
|
|
@Override
|
|
public void afterTextChanged(Editable editable) {
|
|
if (addressOk() && amountOk()) {
|
|
bPrepareSend.setEnabled(true);
|
|
} else {
|
|
bPrepareSend.setEnabled(false);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
|
}
|
|
|
|
@Override
|
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
|
}
|
|
});
|
|
etPaymentId.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
|
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
|
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) {
|
|
if (paymentIdOk()) {
|
|
etAmount.requestFocus();
|
|
} // otherwise ignore
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
|
|
etAmount.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
|
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
|
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) {
|
|
if (amountOk()) {
|
|
Helper.hideKeyboard(getActivity());
|
|
disableEdit();
|
|
prepareSend();
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
etAmount.addTextChangedListener(new TextWatcher() {
|
|
@Override
|
|
public void afterTextChanged(Editable editable) {
|
|
if (addressOk() && amountOk()) {
|
|
bPrepareSend.setEnabled(true);
|
|
} else {
|
|
bPrepareSend.setEnabled(false);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
|
}
|
|
|
|
@Override
|
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
|
}
|
|
});
|
|
|
|
setPrepareButtonState();
|
|
bPrepareSend.setOnClickListener(new View.OnClickListener()
|
|
|
|
{
|
|
@Override
|
|
public void onClick(View v) {
|
|
Helper.hideKeyboard(getActivity());
|
|
disableEdit();
|
|
prepareSend();
|
|
}
|
|
});
|
|
|
|
bDispose.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
activityCallback.onDisposeRequest();
|
|
enableEdit();
|
|
}
|
|
});
|
|
|
|
bScan.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
activityCallback.onScanAddress();
|
|
}
|
|
});
|
|
|
|
bPaymentId.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
etPaymentId.setText((Wallet.generatePaymentId()));
|
|
etPaymentId.setSelection(etPaymentId.getText().length());
|
|
}
|
|
});
|
|
|
|
bSweep.setOnClickListener(new View.OnClickListener()
|
|
|
|
{
|
|
@Override
|
|
public void onClick(View v) {
|
|
Helper.hideKeyboard(getActivity());
|
|
prepareSweep();
|
|
}
|
|
});
|
|
|
|
etNotes.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
|
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
|
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) {
|
|
if (amountOk()) {
|
|
Helper.hideKeyboard(getActivity());
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
|
|
bSend.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
bSend.setEnabled(false);
|
|
boolean testnet = WalletManager.getInstance().isTestNet();
|
|
if (testnet) {
|
|
send();
|
|
} else {
|
|
etNotes.setEnabled(false);
|
|
Handler handler = new Handler();
|
|
handler.postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
bReallySend.setVisibility(View.VISIBLE);
|
|
}
|
|
}, 1000);
|
|
}
|
|
}
|
|
});
|
|
|
|
bReallySend.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
bReallySend.setEnabled(false);
|
|
send();
|
|
}
|
|
});
|
|
return view;
|
|
}
|
|
|
|
private void setPrepareButtonState() {
|
|
if (addressOk() && amountOk() && (bSend.getVisibility() != View.VISIBLE)) {
|
|
bPrepareSend.setEnabled(true);
|
|
} else {
|
|
bPrepareSend.setEnabled(false);
|
|
}
|
|
}
|
|
|
|
private boolean addressOk() {
|
|
String address = etAddress.getText().toString();
|
|
return Helper.isAddressOk(address, WalletManager.getInstance().isTestNet());
|
|
}
|
|
|
|
private boolean amountOk() {
|
|
long amount = Wallet.getAmountFromString(etAmount.getText().toString());
|
|
return (amount > 0);
|
|
}
|
|
|
|
private boolean paymentIdOk() {
|
|
String paymentId = etPaymentId.getText().toString();
|
|
return paymentId.isEmpty() || Wallet.isPaymentIdValid(paymentId);
|
|
}
|
|
|
|
private void prepareSend() {
|
|
String dst_addr = etAddress.getText().toString();
|
|
String paymentId = etPaymentId.getText().toString();
|
|
long amount = Wallet.getAmountFromString(etAmount.getText().toString());
|
|
int mixin = Mixins[sMixin.getSelectedItemPosition()];
|
|
int priorityIndex = sPriority.getSelectedItemPosition();
|
|
PendingTransaction.Priority priority = Priorities[priorityIndex];
|
|
//Log.d(TAG, dst_addr + "/" + paymentId + "/" + amount + "/" + mixin + "/" + priority.toString());
|
|
TxData txData = new TxData(
|
|
dst_addr,
|
|
paymentId,
|
|
amount,
|
|
mixin,
|
|
priority);
|
|
showProgress();
|
|
activityCallback.onPrepareSend(txData);
|
|
}
|
|
|
|
private void prepareSweep() {
|
|
etAddress.setText(activityCallback.getWalletAddress());
|
|
etPaymentId.setText("");
|
|
etAmount.setText("");
|
|
disableEdit();
|
|
showProgress();
|
|
activityCallback.onPrepareSweep();
|
|
}
|
|
|
|
private void disableEdit() {
|
|
sMixin.setEnabled(false);
|
|
sPriority.setEnabled(false);
|
|
etAddress.setEnabled(false);
|
|
etPaymentId.setEnabled(false);
|
|
etAmount.setEnabled(false);
|
|
bScan.setEnabled(false);
|
|
bPaymentId.setEnabled(false);
|
|
bSweep.setEnabled(false);
|
|
bPrepareSend.setEnabled(false);
|
|
}
|
|
|
|
private void enableEdit() {
|
|
sMixin.setEnabled(true);
|
|
sPriority.setEnabled(true);
|
|
etAddress.setEnabled(true);
|
|
etPaymentId.setEnabled(true);
|
|
etAmount.setEnabled(true);
|
|
bScan.setEnabled(true);
|
|
bPaymentId.setEnabled(true);
|
|
bSweep.setEnabled(true);
|
|
bPrepareSend.setEnabled(true);
|
|
llConfirmSend.setVisibility(View.GONE);
|
|
bSend.setEnabled(true);
|
|
etNotes.setEnabled(true);
|
|
bReallySend.setVisibility(View.GONE);
|
|
}
|
|
|
|
private void send() {
|
|
etNotes.setEnabled(false);
|
|
String notes = etNotes.getText().toString();
|
|
activityCallback.onSend(notes);
|
|
}
|
|
|
|
SendFragment.Listener activityCallback;
|
|
|
|
public interface Listener {
|
|
void onPrepareSend(TxData data);
|
|
|
|
void onPrepareSweep();
|
|
|
|
void onSend(String notes);
|
|
|
|
String getWalletAddress();
|
|
|
|
void onDisposeRequest();
|
|
|
|
void onScanAddress();
|
|
|
|
BarcodeData getScannedData();
|
|
|
|
}
|
|
|
|
@Override
|
|
public void onResume() {
|
|
super.onResume();
|
|
Log.d(TAG, "onResume");
|
|
BarcodeData data = activityCallback.getScannedData();
|
|
if (data != null) {
|
|
String scannedAddress = data.address;
|
|
if (scannedAddress != null) {
|
|
etAddress.setText(scannedAddress);
|
|
} else {
|
|
etAddress.getText().clear();
|
|
}
|
|
String scannedPaymenId = data.paymentId;
|
|
if (scannedPaymenId != null) {
|
|
etPaymentId.setText(scannedPaymenId);
|
|
} else {
|
|
etPaymentId.getText().clear();
|
|
}
|
|
if (data.amount >= 0) {
|
|
String scannedAmount = Helper.getDisplayAmount(data.amount);
|
|
etAmount.setText(scannedAmount);
|
|
} else {
|
|
etAmount.getText().clear();
|
|
}
|
|
etAmount.requestFocus();
|
|
etAmount.setSelection(etAmount.getText().length());
|
|
} else { // no scan data
|
|
// jump to first empty field
|
|
if (etAddress.getText().toString().isEmpty()) {
|
|
etAddress.requestFocus();
|
|
} else if (etPaymentId.getText().toString().isEmpty()) {
|
|
etPaymentId.requestFocus();
|
|
} else {
|
|
etAmount.requestFocus();
|
|
etAmount.setSelection(etAmount.getText().length());
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onAttach(Context context) {
|
|
super.onAttach(context);
|
|
if (context instanceof SendFragment.Listener) {
|
|
this.activityCallback = (SendFragment.Listener) context;
|
|
} else {
|
|
throw new ClassCastException(context.toString()
|
|
+ " must implement Listener");
|
|
}
|
|
}
|
|
|
|
public void onCreatedTransaction(PendingTransaction pendingTransaction) {
|
|
hideProgress();
|
|
if (pendingTransaction == null) {
|
|
enableEdit();
|
|
return;
|
|
}
|
|
llConfirmSend.setVisibility(View.VISIBLE);
|
|
tvTxAmount.setText(Wallet.getDisplayAmount(pendingTransaction.getAmount()));
|
|
tvTxFee.setText(Wallet.getDisplayAmount(pendingTransaction.getFee()));
|
|
tvTxDust.setText(Wallet.getDisplayAmount(pendingTransaction.getDust()));
|
|
}
|
|
|
|
public void onCreatedTransactionFailed(String errorText) {
|
|
hideProgress();
|
|
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
|
builder.setTitle(getString(R.string.send_error_title));
|
|
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
enableEdit();
|
|
}
|
|
});
|
|
builder.setMessage(errorText);
|
|
builder.setCancelable(false);
|
|
builder.create().show();
|
|
}
|
|
|
|
public void showProgress() {
|
|
pbProgress.setIndeterminate(true);
|
|
pbProgress.setVisibility(View.VISIBLE);
|
|
}
|
|
|
|
public void hideProgress() {
|
|
pbProgress.setVisibility(View.GONE);
|
|
}
|
|
}
|