wowwallet/app/src/main/java/com/m2049r/xmrwallet/ReceiveFragment.java

404 lines
14 KiB
Java
Raw Normal View History

2017-09-01 16:00:54 -06:00
/*
* 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;
2017-09-01 16:00:54 -06:00
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.AsyncTask;
2017-09-01 16:00:54 -06:00
import android.os.Bundle;
2017-10-31 15:35:23 -06:00
import android.support.design.widget.TextInputLayout;
2017-09-01 16:00:54 -06:00
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.util.Log;
2017-09-01 16:00:54 -06:00
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;
2017-10-31 15:35:23 -06:00
import android.widget.ImageButton;
2017-09-01 16:00:54 -06:00
import android.widget.ImageView;
import android.widget.ProgressBar;
2017-09-01 16:00:54 -06:00
import android.widget.TextView;
import android.widget.Toast;
2017-09-01 16:00:54 -06:00
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
2017-10-31 15:35:23 -06:00
import com.m2049r.xmrwallet.layout.ExchangeView;
import com.m2049r.xmrwallet.layout.Toolbar;
2017-09-01 16:00:54 -06:00
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
2017-09-01 16:00:54 -06:00
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor;
2017-09-01 16:00:54 -06:00
import java.util.HashMap;
import java.util.Map;
2017-10-31 15:35:23 -06:00
public class ReceiveFragment extends Fragment {
2017-09-01 16:00:54 -06:00
static final String TAG = "ReceiveFragment";
private ProgressBar pbProgress;
private TextView tvAddress;
private TextInputLayout etPaymentId;
private ExchangeView evAmount;
private Button bPaymentId;
private Button bGenerate;
private ImageView qrCode;
private EditText etDummy;
private ImageButton bCopyAddress;
2017-09-01 16:00:54 -06:00
2017-10-31 15:35:23 -06:00
//String name;
public interface Listener {
2017-10-31 15:35:23 -06:00
void setToolbarButton(int type);
2017-10-31 15:35:23 -06:00
void setTitle(String title);
void setSubtitle(String subtitle);
}
2017-09-01 16:00:54 -06:00
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
2017-10-31 15:35:23 -06:00
View view = inflater.inflate(R.layout.fragment_receive, container, false);
2017-09-01 16:00:54 -06:00
pbProgress = (ProgressBar) view.findViewById(R.id.pbProgress);
2017-09-01 16:00:54 -06:00
tvAddress = (TextView) view.findViewById(R.id.tvAddress);
2017-10-31 15:35:23 -06:00
etPaymentId = (TextInputLayout) view.findViewById(R.id.etPaymentId);
evAmount = (ExchangeView) view.findViewById(R.id.evAmount);
2017-09-01 16:00:54 -06:00
bPaymentId = (Button) view.findViewById(R.id.bPaymentId);
qrCode = (ImageView) view.findViewById(R.id.qrCode);
bGenerate = (Button) view.findViewById(R.id.bGenerate);
etDummy = (EditText) view.findViewById(R.id.etDummy);
2017-10-31 15:35:23 -06:00
bCopyAddress = (ImageButton) view.findViewById(R.id.bCopyAddress);
2017-09-01 16:00:54 -06:00
2017-10-31 15:35:23 -06:00
etPaymentId.getEditText().setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
2017-09-01 16:00:54 -06:00
2017-10-31 15:35:23 -06:00
bCopyAddress.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
copyAddress();
2017-09-01 16:00:54 -06:00
}
});
2017-10-31 15:35:23 -06:00
evAmount.setOnNewAmountListener(new ExchangeView.OnNewAmountListener() {
2017-09-01 16:00:54 -06:00
@Override
2017-10-31 15:35:23 -06:00
public void onNewAmount(String xmr) {
Log.d(TAG, "new amount = " + xmr);
generateQr();
2017-09-01 16:00:54 -06:00
}
2017-10-31 15:35:23 -06:00
});
2017-09-01 16:00:54 -06:00
2017-10-31 15:35:23 -06:00
evAmount.setOnFailedExchangeListener(new ExchangeView.OnFailedExchangeListener() {
2017-09-01 16:00:54 -06:00
@Override
2017-10-31 15:35:23 -06:00
public void onFailedExchange() {
Toast.makeText(getActivity(), getString(R.string.message_exchange_failed), Toast.LENGTH_LONG).show();
2017-09-01 16:00:54 -06:00
}
});
2017-10-31 15:35:23 -06:00
etPaymentId.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
2017-09-01 16:00:54 -06:00
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
2017-09-03 06:09:27 -06:00
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) {
2017-10-31 15:35:23 -06:00
if (checkPaymentId()) { // && evAmount.checkXmrAmount(true)) {
generateQr();
2017-09-01 16:00:54 -06:00
}
return true;
}
return false;
}
});
2017-10-31 15:35:23 -06:00
etPaymentId.getEditText().addTextChangedListener(new TextWatcher() {
2017-09-01 16:00:54 -06:00
@Override
public void afterTextChanged(Editable editable) {
2017-10-31 15:35:23 -06:00
clearQR();
2017-09-01 16:00:54 -06:00
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
});
bPaymentId.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
2017-10-31 15:35:23 -06:00
etPaymentId.getEditText().setText((Wallet.generatePaymentId()));
etPaymentId.getEditText().setSelection(etPaymentId.getEditText().getText().length());
if (checkPaymentId()) { //&& evAmount.checkXmrAmount(true)) {
generateQr();
2017-09-01 16:00:54 -06:00
}
}
});
bGenerate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
2017-10-31 15:35:23 -06:00
if (checkPaymentId()) {
evAmount.doExchange();
}
}
});
showProgress();
2017-10-31 15:35:23 -06:00
clearQR();
2017-09-01 16:00:54 -06:00
Bundle b = getArguments();
String address = b.getString("address");
2017-10-31 15:35:23 -06:00
String walletName = b.getString("name");
Log.d(TAG, "address=" + address + "/name=" + walletName);
if (address == null) {
String path = b.getString("path");
String password = b.getString("password");
2017-10-31 15:35:23 -06:00
loadAndShow(path, password);
} else {
2017-10-31 15:35:23 -06:00
show(walletName, address);
}
2017-09-01 16:00:54 -06:00
return view;
}
2017-10-31 15:35:23 -06:00
void copyAddress() {
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_address), tvAddress.getText().toString());
Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show();
}
boolean qrValid = true;
void clearQR() {
if (qrValid) {
qrCode.setImageBitmap(getMoneroLogo());
qrValid = false;
}
}
2017-10-31 15:35:23 -06:00
void setQR(Bitmap qr) {
qrCode.setImageBitmap(qr);
qrValid = true;
Helper.hideKeyboard(getActivity());
etDummy.requestFocus();
}
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume()");
2017-10-31 15:35:23 -06:00
listenerCallback.setToolbarButton(Toolbar.BUTTON_BACK);
listenerCallback.setSubtitle(getString(R.string.receive_title));
generateQr();
}
2017-10-31 15:35:23 -06:00
private void show(String name, String address) {
Log.d(TAG, "name=" + name);
listenerCallback.setTitle(name);
tvAddress.setText(address);
etPaymentId.setEnabled(true);
2017-10-31 15:35:23 -06:00
//etAmount.setEnabled(true);
bPaymentId.setEnabled(true);
bGenerate.setEnabled(true);
2017-10-31 15:35:23 -06:00
bCopyAddress.setEnabled(true);
bCopyAddress.setImageResource(R.drawable.ic_content_copy_black_24dp);
hideProgress();
2017-10-31 15:35:23 -06:00
generateQr();
}
2017-10-31 15:35:23 -06:00
private void loadAndShow(String walletPath, String password) {
new AsyncShow().executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR,
walletPath, password);
}
private class AsyncShow extends AsyncTask<String, Void, Boolean> {
String password;
String address;
2017-10-31 15:35:23 -06:00
String name;
@Override
protected Boolean doInBackground(String... params) {
if (params.length != 2) return false;
String walletPath = params[0];
password = params[1];
Wallet wallet = WalletManager.getInstance().openWallet(walletPath, password);
address = wallet.getAddress();
2017-10-31 15:35:23 -06:00
name = wallet.getName();
wallet.close();
return true;
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
2017-10-31 15:35:23 -06:00
if (!isAdded()) return; // never mind
if (result) {
2017-10-31 15:35:23 -06:00
show(name, address);
} else {
Toast.makeText(getActivity(), getString(R.string.receive_cannot_open), Toast.LENGTH_LONG).show();
2017-09-03 06:09:27 -06:00
hideProgress();
}
}
}
2017-10-31 15:35:23 -06:00
private boolean checkPaymentId() {
String paymentId = etPaymentId.getEditText().getText().toString();
boolean ok = paymentId.isEmpty() || Wallet.isPaymentIdValid(paymentId);
2017-10-31 15:35:23 -06:00
if (!ok) {
etPaymentId.setError(getString(R.string.receive_paymentid_invalid));
} else {
etPaymentId.setError(null);
}
return ok;
2017-09-01 16:00:54 -06:00
}
2017-10-31 15:35:23 -06:00
private void generateQr() {
Log.d(TAG, "GENQR");
2017-09-01 16:00:54 -06:00
String address = tvAddress.getText().toString();
2017-10-31 15:35:23 -06:00
String paymentId = etPaymentId.getEditText().getText().toString();
String xmrAmount = evAmount.getAmount();
Log.d(TAG, xmrAmount + "/" + paymentId + "/" + address);
if ((xmrAmount == null) || !Wallet.isAddressValid(address, WalletManager.getInstance().isTestNet())) {
clearQR();
Log.d(TAG, "CLEARQR");
return;
}
2017-09-01 16:00:54 -06:00
StringBuffer sb = new StringBuffer();
sb.append(ScannerFragment.QR_SCHEME).append(address);
boolean first = true;
if (!paymentId.isEmpty()) {
if (first) {
sb.append("?");
first = false;
}
sb.append(ScannerFragment.QR_PAYMENTID).append('=').append(paymentId);
}
if (!xmrAmount.isEmpty()) {
2017-09-01 16:00:54 -06:00
if (first) {
sb.append("?");
} else {
sb.append("&");
}
sb.append(ScannerFragment.QR_AMOUNT).append('=').append(xmrAmount);
2017-09-01 16:00:54 -06:00
}
String text = sb.toString();
Bitmap qr = generate(text, 500, 500);
if (qr != null) {
2017-10-31 15:35:23 -06:00
setQR(qr);
Log.d(TAG, "SETQR");
etDummy.requestFocus();
2017-10-31 15:35:23 -06:00
Helper.hideKeyboard(getActivity());
2017-09-01 16:00:54 -06:00
}
}
public Bitmap generate(String text, int width, int height) {
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
try {
BitMatrix bitMatrix = new QRCodeWriter().encode(text, BarcodeFormat.QR_CODE, width, height, hints);
int[] pixels = new int[width * height];
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
if (bitMatrix.get(j, i)) {
pixels[i * width + j] = 0x00000000;
} else {
pixels[i * height + j] = 0xffffffff;
}
}
}
Bitmap bitmap = Bitmap.createBitmap(pixels, 0, width, width, height, Bitmap.Config.RGB_565);
bitmap = addLogo(bitmap);
return bitmap;
} catch (WriterException e) {
e.printStackTrace();
}
return null;
}
2017-09-02 02:05:50 -06:00
// TODO check if we can sensibly cache some of this
2017-09-01 16:00:54 -06:00
private Bitmap addLogo(Bitmap qrBitmap) {
Bitmap logo = getMoneroLogo();
int qrWidth = qrBitmap.getWidth();
int qrHeight = qrBitmap.getHeight();
int logoWidth = logo.getWidth();
int logoHeight = logo.getHeight();
Bitmap logoBitmap = Bitmap.createBitmap(qrWidth, qrHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(logoBitmap);
canvas.drawBitmap(qrBitmap, 0, 0, null);
canvas.save(Canvas.ALL_SAVE_FLAG);
// figure out how to scale the logo
float scaleSize = 1.0f;
while ((logoWidth / scaleSize) > (qrWidth / 5) || (logoHeight / scaleSize) > (qrHeight / 5)) {
scaleSize *= 2;
}
float sx = 1.0f / scaleSize;
canvas.scale(sx, sx, qrWidth / 2, qrHeight / 2);
canvas.drawBitmap(logo, (qrWidth - logoWidth) / 2, (qrHeight - logoHeight) / 2, null);
canvas.restore();
return logoBitmap;
}
private Bitmap logo = null;
private Bitmap getMoneroLogo() {
if (logo == null) {
logo = Helper.getBitmap(getContext(), R.drawable.ic_monero_logo_b);
2017-09-01 16:00:54 -06:00
}
return logo;
}
public void showProgress() {
pbProgress.setVisibility(View.VISIBLE);
}
public void hideProgress() {
pbProgress.setVisibility(View.GONE);
}
Listener listenerCallback = null;
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof Listener) {
this.listenerCallback = (Listener) context;
} else {
throw new ClassCastException(context.toString()
+ " must implement Listener");
}
}
static final String PREF_CURRENCY_A = "PREF_CURRENCY_A";
static final String PREF_CURRENCY_B = "PREF_CURRENCY_B";
@Override
public void onPause() {
Log.d(TAG, "onPause()");
super.onPause();
}
2017-09-01 16:00:54 -06:00
}