diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0f9eb50..83f1734 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,7 +11,7 @@ diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java index 53610b2..a5d87c3 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java @@ -69,12 +69,9 @@ public class GenerateFragment extends Fragment { bGenerate = (Button) view.findViewById(R.id.bGenerate); etWalletMnemonic.setRawInputType(InputType.TYPE_CLASS_TEXT); - etWalletAddress.setRawInputType(InputType.TYPE_CLASS_TEXT); - etWalletViewKey.setRawInputType(InputType.TYPE_CLASS_TEXT); - etWalletSpendKey.setRawInputType(InputType.TYPE_CLASS_TEXT); - - boolean testnet = WalletManager.getInstance().isTestNet(); - //etWalletMnemonic.setTextIsSelectable(testnet); + etWalletAddress.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + etWalletViewKey.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + etWalletSpendKey.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); Helper.showKeyboard(getActivity()); etWalletName.addTextChangedListener(new TextWatcher() { @@ -298,8 +295,8 @@ public class GenerateFragment extends Fragment { private void generateWallet() { String name = etWalletName.getText().toString(); if (name.length() == 0) return; - String walletPath = Helper.getWalletPath(getActivity(), name); - if (WalletManager.getInstance().walletExists(walletPath)) { + File walletFile = Helper.getWalletFile(getActivity(), name); + if (WalletManager.getInstance().walletExists(walletFile)) { Toast.makeText(getActivity(), getString(R.string.generate_wallet_exists), Toast.LENGTH_LONG).show(); etWalletName.requestFocus(); return; @@ -348,7 +345,7 @@ public class GenerateFragment extends Fragment { @Override public void onResume() { super.onResume(); - Log.d(TAG, "onPause()"); + Log.d(TAG, "onResume()"); activityCallback.setTitle(getString(R.string.generate_title)); } diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java index ece553c..f2624bc 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java @@ -79,10 +79,10 @@ public class GenerateReviewFragment extends Fragment { Bundle b = getArguments(); String type = b.getString("type"); if (!type.equals(VIEW_WALLET)) { - String name = b.getString("name"); + String path = b.getString("path"); String password = b.getString("password"); - tvWalletName.setText(new File(name).getName()); - show(name, password, type); + tvWalletName.setText(new File(path).getName()); + show(path, password, type); } else { show(walletCallback.getWallet(), null, type); } diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java index fc64961..ea9ada6 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java @@ -85,8 +85,8 @@ public class LoginActivity extends AppCompatActivity } Log.d(TAG, "selected wallet is ." + walletName + "."); // now it's getting real, check if wallet exists - String walletPath = Helper.getWalletPath(this, walletName); - if (WalletManager.getInstance().walletExists(walletPath)) { + File walletFile = Helper.getWalletFile(this, walletName); + if (WalletManager.getInstance().walletExists(walletFile)) { promptPassword(walletName, new PasswordAction() { @Override public void action(String walletName, String password) { @@ -99,14 +99,30 @@ public class LoginActivity extends AppCompatActivity } @Override - public void onWalletDetails(final String walletName) { + public void onWalletDetails(String walletName) { Log.d(TAG, "details for wallet ." + walletName + "."); - final String walletPath = Helper.getWalletPath(this, walletName); - if (WalletManager.getInstance().walletExists(walletPath)) { + final File walletFile = Helper.getWalletFile(this, walletName); + if (WalletManager.getInstance().walletExists(walletFile)) { promptPassword(walletName, new PasswordAction() { @Override public void action(String walletName, String password) { - startDetails(walletPath, password, GenerateReviewFragment.VIEW_DETAILS); + startDetails(walletFile, password, GenerateReviewFragment.VIEW_DETAILS); + } + }); + } else { // this cannot really happen as we prefilter choices + Toast.makeText(this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show(); + } + } + + @Override + public void onWalletReceive(String walletName) { + Log.d(TAG, "receive for wallet ." + walletName + "."); + final File walletFile = Helper.getWalletFile(this, walletName); + if (WalletManager.getInstance().walletExists(walletFile)) { + promptPassword(walletName, new PasswordAction() { + @Override + public void action(String walletName, String password) { + startReceive(walletFile, password); } }); } else { // this cannot really happen as we prefilter choices @@ -251,15 +267,23 @@ public class LoginActivity extends AppCompatActivity startActivity(intent); } - void startDetails(final String walletPath, final String password, String type) { + void startDetails(File walletFile, String password, String type) { Log.d(TAG, "startDetails()"); Bundle b = new Bundle(); - b.putString("name", walletPath); + b.putString("path", walletFile.getAbsolutePath()); b.putString("password", password); b.putString("type", type); startReviewFragment(b); } + void startReceive(File walletFile, String password) { + Log.d(TAG, "startReceive()"); + Bundle b = new Bundle(); + b.putString("path", walletFile.getAbsolutePath()); + b.putString("password", password); + startReceiveFragment(b); + } + @Override public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { Log.d(TAG, "onRequestPermissionsResult()"); @@ -297,6 +321,11 @@ public class LoginActivity extends AppCompatActivity Log.d(TAG, "GenerateReviewFragment placed"); } + void startReceiveFragment(Bundle extras) { + replaceFragment(new ReceiveFragment(), null, extras); + Log.d(TAG, "ReceiveFragment placed"); + } + void replaceFragment(Fragment newFragment, String stackName, Bundle extras) { if (extras != null) { newFragment.setArguments(extras); @@ -345,29 +374,29 @@ public class LoginActivity extends AppCompatActivity return; } - String newWalletPath = new File(newWalletFolder, name).getAbsolutePath(); - boolean success = walletCreator.createWallet(newWalletPath, password); + File newWalletFile = new File(newWalletFolder, name); + boolean success = walletCreator.createWallet(newWalletFile, password); if (success) { - startDetails(newWalletPath, password, GenerateReviewFragment.VIEW_ACCEPT); + startDetails(newWalletFile, password, GenerateReviewFragment.VIEW_ACCEPT); } else { Toast.makeText(LoginActivity.this, getString(R.string.generate_wallet_create_failed), Toast.LENGTH_LONG).show(); - Log.e(TAG, "Could not create new wallet in " + newWalletPath); + Log.e(TAG, "Could not create new wallet in " + newWalletFile.getAbsolutePath()); } } interface WalletCreator { - boolean createWallet(String path, String password); + boolean createWallet(File aFile, String password); } @Override public void onGenerate(String name, String password) { createWallet(name, password, new WalletCreator() { - public boolean createWallet(String path, String password) { + public boolean createWallet(File aFile, String password) { Wallet newWallet = WalletManager.getInstance() - .createWallet(path, password, MNEMONIC_LANGUAGE); + .createWallet(aFile, password, MNEMONIC_LANGUAGE); boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok); if (!success) Log.e(TAG, newWallet.getErrorString()); newWallet.close(); @@ -380,8 +409,8 @@ public class LoginActivity extends AppCompatActivity public void onGenerate(String name, String password, final String seed, final long restoreHeight) { createWallet(name, password, new WalletCreator() { - public boolean createWallet(String path, String password) { - Wallet newWallet = WalletManager.getInstance().recoveryWallet(path, seed, restoreHeight); + public boolean createWallet(File aFile, String password) { + Wallet newWallet = WalletManager.getInstance().recoveryWallet(aFile, seed, restoreHeight); boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok); if (!success) Log.e(TAG, newWallet.getErrorString()); newWallet.setPassword(password); @@ -397,9 +426,9 @@ public class LoginActivity extends AppCompatActivity final String address, final String viewKey, final String spendKey, final long restoreHeight) { createWallet(name, password, new WalletCreator() { - public boolean createWallet(String path, String password) { + public boolean createWallet(File aFile, String password) { Wallet newWallet = WalletManager.getInstance() - .createWalletFromKeys(path, MNEMONIC_LANGUAGE, restoreHeight, + .createWalletFromKeys(aFile, MNEMONIC_LANGUAGE, restoreHeight, address, viewKey, spendKey); boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok); if (!success) Log.e(TAG, newWallet.getErrorString()); diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java index 4515ada..70c6055 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java @@ -86,6 +86,8 @@ public class LoginFragment extends Fragment { void onWalletDetails(final String wallet); + void onWalletReceive(final String wallet); + void onAddWallet(); void setTitle(String title); @@ -117,7 +119,7 @@ public class LoginFragment extends Fragment { @Override public void onResume() { super.onResume(); - Log.d(TAG, "onPause()"); + Log.d(TAG, "onResume()"); activityCallback.setTitle(getString(R.string.login_activity_name)); } @@ -408,17 +410,18 @@ public class LoginFragment extends Fragment { @Override public boolean onContextItemSelected(MenuItem item) { AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); + String listItem = (String) listView.getItemAtPosition(info.position); switch (item.getItemId()) { case R.id.action_info: - String listItem = (String) listView.getItemAtPosition(info.position); return showInfo(listItem); + case R.id.action_receive: + return showReceive(listItem); default: return super.onContextItemSelected(item); } } private boolean showInfo(String listItem) { - if (listItem.length() <= (WALLETNAME_PREAMBLE_LENGTH)) { Toast.makeText(getActivity(), getString(R.string.panic), Toast.LENGTH_LONG).show(); return true; @@ -436,4 +439,23 @@ public class LoginFragment extends Fragment { activityCallback.onWalletDetails(wallet); return true; } + + private boolean showReceive(String listItem) { + if (listItem.length() <= (WALLETNAME_PREAMBLE_LENGTH)) { + Toast.makeText(getActivity(), getString(R.string.panic), Toast.LENGTH_LONG).show(); + return true; + } + + String wallet = listItem.substring(WALLETNAME_PREAMBLE_LENGTH); + String x = isMainNet() ? "4" : "9A"; + if (x.indexOf(listItem.charAt(1)) < 0) { + Toast.makeText(getActivity(), getString(R.string.prompt_wrong_net), Toast.LENGTH_LONG).show(); + return true; + } + + checkAndSetWalletDaemon("", !isMainNet()); // just set selected net + + activityCallback.onWalletReceive(wallet); + return true; + } } diff --git a/app/src/main/java/com/m2049r/xmrwallet/ReceiveFragment.java b/app/src/main/java/com/m2049r/xmrwallet/ReceiveFragment.java new file mode 100644 index 0000000..49f255b --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/ReceiveFragment.java @@ -0,0 +1,332 @@ +/* + * 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.graphics.Bitmap; +import android.graphics.Canvas; +import android.os.Bundle; +import android.support.v4.app.Fragment; +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.ImageView; +import android.widget.ProgressBar; +import android.widget.TextView; + +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; +import com.m2049r.xmrwallet.model.Wallet; +import com.m2049r.xmrwallet.model.WalletManager; +import com.m2049r.xmrwallet.service.MoneroHandlerThread; +import com.m2049r.xmrwallet.util.Helper; + +import java.util.HashMap; +import java.util.Map; + +public class ReceiveFragment extends Fragment { + static final String TAG = "ReceiveFragment"; + + ProgressBar pbProgress; + TextView tvAddress; + EditText etPaymentId; + EditText etAmount; + Button bPaymentId; + Button bGenerate; + ImageView qrCode; + EditText etDummy; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View view = inflater.inflate(R.layout.receive_fragment, container, false); + + pbProgress = (ProgressBar) view.findViewById(R.id.pbProgress); + tvAddress = (TextView) view.findViewById(R.id.tvAddress); + etPaymentId = (EditText) view.findViewById(R.id.etPaymentId); + etAmount = (EditText) view.findViewById(R.id.etAmount); + 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); + + etPaymentId.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + + Helper.showKeyboard(getActivity()); + etPaymentId.requestFocus(); + 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; + } + }); + etPaymentId.addTextChangedListener(new TextWatcher() { + @Override + public void afterTextChanged(Editable editable) { + qrCode.setImageBitmap(getMoneroLogo()); + if (paymentIdOk() && amountOk()) { + bGenerate.setEnabled(true); + } else { + bGenerate.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) { + } + }); + + 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_NEXT)) { + if (paymentIdOk() && amountOk()) { + Helper.hideKeyboard(getActivity()); + generateQr(); + } + return true; + } + return false; + } + }); + etAmount.addTextChangedListener(new TextWatcher() { + @Override + public void afterTextChanged(Editable editable) { + qrCode.setImageBitmap(getMoneroLogo()); + if (paymentIdOk() && amountOk()) { + bGenerate.setEnabled(true); + } else { + bGenerate.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) { + } + }); + + bPaymentId.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + etPaymentId.setText((Wallet.generatePaymentId())); + etPaymentId.setSelection(etPaymentId.getText().length()); + if (paymentIdOk() && amountOk()) { + generateQr(); + } + } + }); + + bGenerate.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (paymentIdOk() && amountOk()) { + Helper.hideKeyboard(getActivity()); + generateQr(); + } + } + }); + + showProgress(); + qrCode.setImageBitmap(getMoneroLogo()); + + Bundle b = getArguments(); + String address = b.getString("address"); + if (address == null) { + String path = b.getString("path"); + String password = b.getString("password"); + show(path, password); + } else { + show(address); + } + return view; + } + + @Override + public void onResume() { + super.onResume(); + Log.d(TAG, "onResume()"); + if (paymentIdOk() && amountOk() && tvAddress.getText().length() > 0) { + generateQr(); + } + } + + + private void show(String address) { + tvAddress.setText(address); + etPaymentId.setEnabled(true); + etAmount.setEnabled(true); + bPaymentId.setEnabled(true); + bGenerate.setEnabled(true); + hideProgress(); + generateQr(); + } + + private void show(final String walletPath, final String password) { + new Thread(null, + new Runnable() { + @Override + public void run() { + final Wallet wallet = WalletManager.getInstance().openWallet(walletPath, password); + getActivity().runOnUiThread(new Runnable() { + public void run() { + show(wallet.getAddress()); + wallet.close(); + } + }); + } + } + , "Receive", MoneroHandlerThread.THREAD_STACK_SIZE).start(); + } + + private boolean amountOk() { + String amountEntry = etAmount.getText().toString(); + if (amountEntry.isEmpty()) return true; + long amount = Wallet.getAmountFromString(amountEntry); + return (amount > 0); + } + + private boolean paymentIdOk() { + String paymentId = etPaymentId.getText().toString(); + return paymentId.isEmpty() || Wallet.isPaymentIdValid(paymentId); + } + + private void generateQr() { + String address = tvAddress.getText().toString(); + String paymentId = etPaymentId.getText().toString(); + String enteredAmount = etAmount.getText().toString(); + // that's a lot of converting ... + String amount = (enteredAmount.isEmpty() ? enteredAmount : Helper.getDisplayAmount(Wallet.getAmountFromString(enteredAmount))); + 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 (!amount.isEmpty()) { + if (first) { + sb.append("?"); + } else { + sb.append("&"); + } + sb.append(ScannerFragment.QR_AMOUNT).append('=').append(amount); + } + String text = sb.toString(); + Bitmap qr = generate(text, 500, 500); + if (qr != null) { + etAmount.setText(amount); + qrCode.setImageBitmap(qr); + etDummy.requestFocus(); + } + } + + public Bitmap generate(String text, int width, int height) { + Map 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; + } + + // TODO check if we can sensibly cache some of this + 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_qr); + } + return logo; + } + + public void showProgress() { + pbProgress.setIndeterminate(true); + pbProgress.setVisibility(View.VISIBLE); + } + + public void hideProgress() { + pbProgress.setVisibility(View.GONE); + } + +} diff --git a/app/src/main/java/com/m2049r/xmrwallet/SendFragment.java b/app/src/main/java/com/m2049r/xmrwallet/SendFragment.java index 72be4a4..d5617d7 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/SendFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/SendFragment.java @@ -100,8 +100,8 @@ public class SendFragment extends Fragment { pbProgress = (ProgressBar) view.findViewById(R.id.pbProgress); - etAddress.setRawInputType(InputType.TYPE_CLASS_TEXT); - etPaymentId.setRawInputType(InputType.TYPE_CLASS_TEXT); + 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()); @@ -209,7 +209,8 @@ public class SendFragment extends Fragment { bPaymentId.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - etPaymentId.setText((activityCallback.generatePaymentId())); + etPaymentId.setText((Wallet.generatePaymentId())); + etPaymentId.setSelection(etPaymentId.getText().length()); } }); @@ -285,7 +286,7 @@ public class SendFragment extends Fragment { private boolean paymentIdOk() { String paymentId = etPaymentId.getText().toString(); - return paymentId.isEmpty() || activityCallback.isPaymentIdValid(paymentId); + return paymentId.isEmpty() || Wallet.isPaymentIdValid(paymentId); } private void prepareSend() { @@ -358,10 +359,6 @@ public class SendFragment extends Fragment { void onSend(String notes); - String generatePaymentId(); - - boolean isPaymentIdValid(String paymentId); - String getWalletAddress(); void onDisposeRequest(); diff --git a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java index 9b71fae..6690bad 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java @@ -47,6 +47,7 @@ import com.m2049r.xmrwallet.util.BarcodeData; import com.m2049r.xmrwallet.util.Helper; import com.m2049r.xmrwallet.util.TxData; +import java.io.File; import java.util.HashMap; import java.util.Map; @@ -120,11 +121,8 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment. } @Override - public boolean onCreateOptionsMenu(Menu menu) { - if (!haveWallet) return true; - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.wallet_menu, menu); - return true; + public boolean hasWallet() { + return haveWallet; } @Override @@ -132,14 +130,16 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment. switch (item.getItemId()) { case R.id.action_info: onWalletDetails(); - break; + return true; + case R.id.action_receive: + onWalletReceive(); + return true; default: - break; + return super.onOptionsItemSelected(item); } - - return true; } + @Override protected void onCreate(Bundle savedInstanceState) { Log.d(TAG, "onCreate()"); @@ -561,16 +561,6 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment. } } - @Override - public String generatePaymentId() { - return getWallet().generatePaymentId(); - } - - @Override - public boolean isPaymentIdValid(String paymentId) { - return Wallet.isPaymentIdValid(paymentId); - } - @Override public String getWalletAddress() { return getWallet().getAddress(); @@ -595,12 +585,9 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment. } private void onWalletDetails() { - Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container); - if (!(fragment instanceof GenerateReviewFragment)) { - Bundle extras = new Bundle(); - extras.putString("type", GenerateReviewFragment.VIEW_WALLET); - replaceFragment(new GenerateReviewFragment(), null, extras); - } + Bundle extras = new Bundle(); + extras.putString("type", GenerateReviewFragment.VIEW_WALLET); + replaceFragment(new GenerateReviewFragment(), null, extras); } @Override @@ -610,11 +597,8 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment. private void startScanFragment() { - Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container); - if (fragment instanceof SendFragment) { - Bundle extras = new Bundle(); - replaceFragment(new ScannerFragment(), null, extras); - } + Bundle extras = new Bundle(); + replaceFragment(new ScannerFragment(), null, extras); } /// QR scanner callbacks @@ -713,4 +697,21 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment. } } + @Override + public void onWalletReceive() { + startReceive(getWalletAddress()); + } + + void startReceive(String address) { + Log.d(TAG, "startReceive()"); + Bundle b = new Bundle(); + b.putString("address", address); + startReceiveFragment(b); + } + + void startReceiveFragment(Bundle extras) { + replaceFragment(new ReceiveFragment(), null, extras); + Log.d(TAG, "ReceiveFragment placed"); + } + } \ No newline at end of file diff --git a/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java b/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java index ca53a84..0fcaa1f 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java @@ -18,12 +18,16 @@ package com.m2049r.xmrwallet; import android.content.Context; import android.os.Bundle; +import android.support.annotation.Nullable; import android.support.constraint.ConstraintLayout; import android.support.v4.app.Fragment; import android.support.v7.widget.DividerItemDecoration; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.Button; @@ -53,6 +57,19 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O ProgressBar pbProgress; Button bSend; + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + if (activityCallback.hasWallet()) + inflater.inflate(R.menu.wallet_menu, menu); + super.onCreateOptionsMenu(menu, inflater); + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -236,6 +253,10 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O boolean isWatchOnly(); String getTxKey(String txId); + + void onWalletReceive(); + + boolean hasWallet(); } @Override diff --git a/app/src/main/java/com/m2049r/xmrwallet/model/WalletManager.java b/app/src/main/java/com/m2049r/xmrwallet/model/WalletManager.java index 3cf5fb5..12bb13e 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/model/WalletManager.java +++ b/app/src/main/java/com/m2049r/xmrwallet/model/WalletManager.java @@ -71,8 +71,8 @@ public class WalletManager { managedWallets.remove(walletId); } - public Wallet createWallet(String path, String password, String language) { - long walletHandle = createWalletJ(path, password, language, isTestNet()); + public Wallet createWallet(File aFile, String password, String language) { + long walletHandle = createWalletJ(aFile.getAbsolutePath(), password, language, isTestNet()); Wallet wallet = new Wallet(walletHandle); manageWallet(wallet.getName(), wallet); return wallet; @@ -89,14 +89,14 @@ public class WalletManager { private native long openWalletJ(String path, String password, boolean isTestNet); - public Wallet recoveryWallet(String path, String mnemonic) { - Wallet wallet = recoveryWallet(path, mnemonic, 0); + public Wallet recoveryWallet(File aFile, String mnemonic) { + Wallet wallet = recoveryWallet(aFile, mnemonic, 0); manageWallet(wallet.getName(), wallet); return wallet; } - public Wallet recoveryWallet(String path, String mnemonic, long restoreHeight) { - long walletHandle = recoveryWalletJ(path, mnemonic, isTestNet(), restoreHeight); + public Wallet recoveryWallet(File aFile, String mnemonic, long restoreHeight) { + long walletHandle = recoveryWalletJ(aFile.getAbsolutePath(), mnemonic, isTestNet(), restoreHeight); Wallet wallet = new Wallet(walletHandle); manageWallet(wallet.getName(), wallet); return wallet; @@ -104,9 +104,9 @@ public class WalletManager { private native long recoveryWalletJ(String path, String mnemonic, boolean isTestNet, long restoreHeight); - public Wallet createWalletFromKeys(String path, String language, long restoreHeight, + public Wallet createWalletFromKeys(File aFile, String language, long restoreHeight, String addressString, String viewKeyString, String spendKeyString) { - long walletHandle = createWalletFromKeysJ(path, language, isTestNet(), restoreHeight, + long walletHandle = createWalletFromKeysJ(aFile.getAbsolutePath(), language, isTestNet(), restoreHeight, addressString, viewKeyString, spendKeyString); Wallet wallet = new Wallet(walletHandle); manageWallet(wallet.getName(), wallet); @@ -134,6 +134,10 @@ public class WalletManager { return closed; } + public boolean walletExists(File aFile) { + return walletExists(aFile.getAbsolutePath()); + } + public native boolean walletExists(String path); public native boolean verifyWalletPassword(String keys_file_name, String password, boolean watch_only); @@ -146,6 +150,31 @@ public class WalletManager { public String address; } + public WalletInfo getWalletInfo(File wallet) { + WalletInfo info = new WalletInfo(); + info.path = wallet.getParentFile(); + info.name = wallet.getName(); + File addressFile = new File(info.path, info.name + ".address.txt"); + //Log.d(TAG, addressFile.getAbsolutePath()); + info.address = "??????"; + BufferedReader addressReader = null; + try { + addressReader = new BufferedReader(new FileReader(addressFile)); + info.address = addressReader.readLine(); + } catch (IOException ex) { + Log.d(TAG, ex.getLocalizedMessage()); + } finally { + if (addressReader != null) { + try { + addressReader.close(); + } catch (IOException ex) { + // that's just too bad + } + } + } + return info; + } + public List findWallets(File path) { List wallets = new ArrayList<>(); Log.d(TAG, "Scanning: " + path.getAbsolutePath()); @@ -155,29 +184,9 @@ public class WalletManager { } }); for (int i = 0; i < found.length; i++) { - WalletInfo info = new WalletInfo(); - info.path = path; String filename = found[i].getName(); - info.name = filename.substring(0, filename.length() - 5); // 5 is length of ".keys"+1 - File addressFile = new File(path, info.name + ".address.txt"); - //Log.d(TAG, addressFile.getAbsolutePath()); - info.address = "??????"; - BufferedReader addressReader = null; - try { - addressReader = new BufferedReader(new FileReader(addressFile)); - info.address = addressReader.readLine(); - } catch (IOException ex) { - Log.d(TAG, ex.getLocalizedMessage()); - } finally { - if (addressReader != null) { - try { - addressReader.close(); - } catch (IOException ex) { - // that's just too bad - } - } - } - wallets.add(info); + File f = new File(found[i].getParent(), filename.substring(0, filename.length() - 5)); // 5 is length of ".keys"+1 + wallets.add(getWalletInfo(f)); } return wallets; } diff --git a/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java b/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java index 5883604..7e6de93 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java +++ b/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java @@ -527,7 +527,7 @@ public class WalletService extends Service { } private Wallet openWallet(String walletName, String walletPassword) { - String path = Helper.getWalletPath(getApplicationContext(), walletName); + String path = Helper.getWalletFile(getApplicationContext(), walletName).getAbsolutePath(); showProgress(20); Wallet wallet = null; WalletManager walletMgr = WalletManager.getInstance(); @@ -557,7 +557,7 @@ public class WalletService extends Service { PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); Notification notification = new Notification.Builder(this) .setContentTitle(getString(R.string.service_description)) - .setSmallIcon(R.drawable.ic_notification_sync_32_32) + .setSmallIcon(R.drawable.ic_monero_32dp) .setContentIntent(pendingIntent) .build(); startForeground(NOTIFICATION_ID, notification); diff --git a/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java b/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java index f2b6b2a..e3fdee3 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java +++ b/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java @@ -21,7 +21,14 @@ import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.VectorDrawable; import android.os.Environment; +import android.support.v4.content.ContextCompat; import android.util.Log; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; @@ -91,12 +98,16 @@ public class Helper { } } - static public String getWalletPath(Context context, String aWalletName) { +// static public String getWalletPath(Context context, String aWalletName) { +// return getWalletFile(context, aWalletName).getAbsolutePath(); +// } + + static public File getWalletFile(Context context, String aWalletName) { File walletDir = getStorageRoot(context); //d(TAG, "walletdir=" + walletDir.getAbsolutePath()); File f = new File(walletDir, aWalletName); Log.d(TAG, "wallet = " + f.getAbsolutePath() + " size=" + f.length()); - return f.getAbsolutePath(); + return f; } /* Checks if external storage is available for read and write */ @@ -136,6 +147,7 @@ public class Helper { return ((address.length() == 95) && ("4".indexOf(address.charAt(0)) >= 0)); } } + static public String getDisplayAmount(long amount) { String s = Wallet.getDisplayAmount(amount); int lastZero = 0; @@ -152,4 +164,25 @@ public class Helper { int cutoff = Math.max(lastZero, decimal + 2); return s.substring(0, cutoff); } + + public static Bitmap getBitmap(Context context, int drawableId) { + Drawable drawable = ContextCompat.getDrawable(context, drawableId); + if (drawable instanceof BitmapDrawable) { + return BitmapFactory.decodeResource(context.getResources(), drawableId); + } else if (drawable instanceof VectorDrawable) { + return getBitmap((VectorDrawable) drawable); + } else { + throw new IllegalArgumentException("unsupported drawable type"); + } + } + + private static Bitmap getBitmap(VectorDrawable vectorDrawable) { + Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), + vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + vectorDrawable.draw(canvas); + return bitmap; + } + } diff --git a/app/src/main/res/drawable/ic_monero_32dp.xml b/app/src/main/res/drawable/ic_monero_32dp.xml new file mode 100644 index 0000000..b5f12a4 --- /dev/null +++ b/app/src/main/res/drawable/ic_monero_32dp.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_monero_qr.xml b/app/src/main/res/drawable/ic_monero_qr.xml new file mode 100644 index 0000000..fd83b04 --- /dev/null +++ b/app/src/main/res/drawable/ic_monero_qr.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_monero_qr_24dp.xml b/app/src/main/res/drawable/ic_monero_qr_24dp.xml new file mode 100644 index 0000000..54af39d --- /dev/null +++ b/app/src/main/res/drawable/ic_monero_qr_24dp.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_notification_sync_32_32.png b/app/src/main/res/drawable/ic_notification_sync_32_32.png deleted file mode 100644 index 4b88148..0000000 Binary files a/app/src/main/res/drawable/ic_notification_sync_32_32.png and /dev/null differ diff --git a/app/src/main/res/drawable/xmr_logo_256.png b/app/src/main/res/drawable/xmr_logo_256.png deleted file mode 100644 index 8abfb92..0000000 Binary files a/app/src/main/res/drawable/xmr_logo_256.png and /dev/null differ diff --git a/app/src/main/res/layout/gen_review_fragment.xml b/app/src/main/res/layout/gen_review_fragment.xml index 3650834..56891ac 100644 --- a/app/src/main/res/layout/gen_review_fragment.xml +++ b/app/src/main/res/layout/gen_review_fragment.xml @@ -4,19 +4,13 @@ android:layout_height="match_parent" android:orientation="vertical"> - - - - + android:visibility="invisible" /> + + + + + + + + + + + + +