1
mirror of https://github.com/m2049r/xmrwallet synced 2025-09-08 20:40:51 +02:00

Compare commits

..

12 Commits
v0.5 ... v0.5.2

Author SHA1 Message Date
m2049r
49e338e80d Merge pull request #31 from m2049r/doc
Update FAQ + version
2017-08-31 18:22:02 +02:00
m2049r
075ddff226 update FAQ + version 2017-08-31 18:19:59 +02:00
m2049r
7eaf17d48e Merge pull request #30 from m2049r/feature_qr_amount
QR scan of amount
2017-08-31 09:58:21 +02:00
m2049r
6822aa83d8 QR scan of amount as well
bugfix: use getSupportFragmentManager everywhere
2017-08-31 09:57:14 +02:00
m2049r
20dbaac4fa Merge pull request #29 from m2049r/bugfix_fragment_v4
use v4 fragment support library to cater for android 5.1
2017-08-31 01:00:52 +02:00
m2049r
726887af2e use v4 fragment support library to cater for android 5.1 2017-08-31 01:00:27 +02:00
m2049r
03da2880ac Merge pull request #28 from m2049r/bugfix_service_timing
make user wait for service to finish
2017-08-30 23:36:01 +02:00
m2049r
78c053f4e8 make user wait for service to finish 2017-08-30 23:35:16 +02:00
m2049r
5b38cc438c Merge pull request #27 from m2049r/docs
update docs and version
2017-08-30 22:24:49 +02:00
m2049r
1eac363102 update docs and version 2017-08-30 22:24:24 +02:00
m2049r
e51425bc2e Merge pull request #26 from m2049r/feature_qr
QR Scanning incl. payment id
2017-08-30 22:02:34 +02:00
m2049r
f5535dbefa QR Scanning incl. payment id 2017-08-30 21:36:07 +02:00
24 changed files with 528 additions and 125 deletions

10
.idea/libraries/core_1_9_8.xml generated Normal file
View File

@@ -0,0 +1,10 @@
<component name="libraryTable">
<library name="core-1.9.8">
<CLASSES>
<root url="jar://$USER_HOME$/.android/build-cache/db7eb580bfa6467818ef12c5f1fa42273b50aa3a/output/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.android/build-cache/db7eb580bfa6467818ef12c5f1fa42273b50aa3a/output/res" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

11
.idea/libraries/core_3_3_0.xml generated Normal file
View File

@@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="core-3.3.0">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.zxing/core/3.3.0/73c49077166faa4c3c0059c5f583d1d7bd1475fe/core-3.3.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.zxing/core/3.3.0/39d966e052e28fc7d83793b02e0af97ccf955745/core-3.3.0-sources.jar!/" />
</SOURCES>
</library>
</component>

10
.idea/libraries/zxing_1_9_8.xml generated Normal file
View File

@@ -0,0 +1,10 @@
<component name="libraryTable">
<library name="zxing-1.9.8">
<CLASSES>
<root url="jar://$USER_HOME$/.android/build-cache/8d8f2e0e10c7af73321454f6fa481e8e5438e654/output/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.android/build-cache/8d8f2e0e10c7af73321454f6fa481e8e5438e654/output/res" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@@ -7,8 +7,8 @@ android {
applicationId "com.m2049r.xmrwallet"
minSdkVersion 21
targetSdkVersion 25
versionCode 7
versionName "0.5"
versionCode 9
versionName "0.5.2"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
@@ -46,5 +46,6 @@ dependencies {
compile 'com.android.support:support-v4:25.3.1'
compile 'com.android.support:recyclerview-v7:25.3.1'
compile 'com.android.support:cardview-v7:25.3.1'
compile 'me.dm7.barcodescanner:zxing:1.9.8'
testCompile 'junit:junit:4.12'
}

View File

@@ -7,6 +7,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.CAMERA" />
<application
android:allowBackup="true"

View File

@@ -16,9 +16,9 @@
package com.m2049r.xmrwallet;
import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;

View File

@@ -16,9 +16,9 @@
package com.m2049r.xmrwallet;
import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

View File

@@ -17,9 +17,6 @@
package com.m2049r.xmrwallet;
import android.app.AlertDialog;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -27,6 +24,10 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
@@ -40,6 +41,7 @@ import android.widget.Toast;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.service.WalletService;
import com.m2049r.xmrwallet.util.Helper;
import java.io.File;
@@ -77,6 +79,10 @@ public class LoginActivity extends AppCompatActivity
@Override
public void onWalletSelected(final String walletName) {
if (WalletService.Running) {
Toast.makeText(this, getString(R.string.service_busy), Toast.LENGTH_SHORT).show();
return;
}
Log.d(TAG, "selected wallet is ." + walletName + ".");
// now it's getting real, check if wallet exists
String walletPath = Helper.getWalletPath(this, walletName);
@@ -276,7 +282,7 @@ public class LoginActivity extends AppCompatActivity
void startLoginFragment() {
Fragment fragment = new LoginFragment();
getFragmentManager().beginTransaction()
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, fragment).commit();
Log.d(TAG, "LoginFragment added");
}
@@ -295,14 +301,14 @@ public class LoginActivity extends AppCompatActivity
if (extras != null) {
newFragment.setArguments(extras);
}
FragmentTransaction transaction = getFragmentManager().beginTransaction();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(stackName);
transaction.commit();
}
void popFragmentStack(String name) {
getFragmentManager().popBackStack(name, FragmentManager.POP_BACK_STACK_INCLUSIVE);
getSupportFragmentManager().popBackStack(name, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
//////////////////////////////////////////
@@ -312,7 +318,7 @@ public class LoginActivity extends AppCompatActivity
public void createWallet(final String name, final String password, final WalletCreator walletCreator) {
final GenerateFragment genFragment = (GenerateFragment)
getFragmentManager().findFragmentById(R.id.fragment_container);
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
File newWalletFolder = new File(getStorageRoot(), ".new");
if (!newWalletFolder.exists()) {
if (!newWalletFolder.mkdir()) {

View File

@@ -16,12 +16,12 @@
package com.m2049r.xmrwallet;
import android.app.Fragment;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.StrictMode;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.ContextMenu;
import android.view.KeyEvent;

View File

@@ -0,0 +1,112 @@
/*
* Copyright (c) 2017 dm77, 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.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result;
import com.m2049r.xmrwallet.util.BarcodeData;
import me.dm7.barcodescanner.zxing.ZXingScannerView;
public class ScannerFragment extends Fragment implements ZXingScannerView.ResultHandler {
static final String TAG = "ScannerFragment";
Listener activityCallback;
public interface Listener {
boolean onAddressScanned(String uri);
}
private ZXingScannerView mScannerView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(TAG, "onCreateView");
mScannerView = new ZXingScannerView(getActivity());
return mScannerView;
}
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume");
mScannerView.setResultHandler(this);
mScannerView.startCamera();
}
static final String QR_SCHEME = "monero:";
static final String QR_PAYMENTID = "tx_payment_id";
static final String QR_AMOUNT = "tx_amount";
@Override
public void handleResult(Result rawResult) {
//Log.d(TAG, rawResult.getBarcodeFormat().toString() + "/" + rawResult.getText());
if ((rawResult.getBarcodeFormat() == BarcodeFormat.QR_CODE) &&
(rawResult.getText().startsWith(QR_SCHEME))) {
if (activityCallback.onAddressScanned(rawResult.getText())) {
return;
} else {
Toast.makeText(getActivity(), getString(R.string.send_qr_address_invalid), Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(getActivity(), getString(R.string.send_qr_invalid), Toast.LENGTH_SHORT).show();
}
// Note from dm77:
// * Wait 2 seconds to resume the preview.
// * On older devices continuously stopping and resuming camera preview can result in freezing the app.
// * I don't know why this is the case but I don't have the time to figure out.
Handler handler = new Handler();
handler.postDelayed(new
Runnable() {
@Override
public void run() {
mScannerView.resumeCameraPreview(ScannerFragment.this);
}
}, 2000);
}
@Override
public void onPause() {
Log.d(TAG, "onPause");
mScannerView.stopCamera();
super.onPause();
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
//Log.d(TAG, "attaching scan");
if (context instanceof Listener) {
this.activityCallback = (Listener) context;
} else {
throw new ClassCastException(context.toString()
+ " must implement Listener");
}
}
}

View File

@@ -16,14 +16,16 @@
package com.m2049r.xmrwallet;
import android.app.Fragment;
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;
@@ -40,14 +42,16 @@ 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 = "GenerateFragment";
static final String TAG = "SendFragment";
EditText etAddress;
EditText etPaymentId;
EditText etAmount;
Button bAddress;
Button bSweep;
Spinner sMixin;
Spinner sPriority;
@@ -80,6 +84,7 @@ public class SendFragment extends Fragment {
etAddress = (EditText) view.findViewById(R.id.etAddress);
etPaymentId = (EditText) view.findViewById(R.id.etPaymentId);
etAmount = (EditText) view.findViewById(R.id.etAmount);
bAddress = (Button) view.findViewById(R.id.bAddress);
bSweep = (Button) view.findViewById(R.id.bSweep);
bPrepareSend = (Button) view.findViewById(R.id.bPrepareSend);
bPaymentId = (Button) view.findViewById(R.id.bPaymentId);
@@ -194,6 +199,13 @@ public class SendFragment extends Fragment {
}
});
bAddress.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
activityCallback.onScanAddress();
}
});
bPaymentId.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -231,7 +243,14 @@ public class SendFragment extends Fragment {
if (testnet) {
send();
} else {
bReallySend.setVisibility(View.VISIBLE);
etNotes.setEnabled(false);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
bReallySend.setVisibility(View.VISIBLE);
}
}, 1000);
}
}
});
@@ -256,11 +275,7 @@ public class SendFragment extends Fragment {
private boolean addressOk() {
String address = etAddress.getText().toString();
if (WalletManager.getInstance().isTestNet()) {
return ((address.length() == 95) && ("9A".indexOf(address.charAt(0)) >= 0));
} else {
return ((address.length() == 95) && ("4".indexOf(address.charAt(0)) >= 0));
}
return Helper.isAddressOk(address, WalletManager.getInstance().isTestNet());
}
private boolean amountOk() {
@@ -321,6 +336,8 @@ public class SendFragment extends Fragment {
bSweep.setEnabled(true);
bPrepareSend.setEnabled(true);
llConfirmSend.setVisibility(View.GONE);
bSend.setEnabled(true);
etNotes.setEnabled(true);
bReallySend.setVisibility(View.GONE);
}
@@ -347,6 +364,49 @@ public class SendFragment extends Fragment {
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

View File

@@ -16,28 +16,23 @@
package com.m2049r.xmrwallet;
import android.app.Fragment;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.InputType;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.m2049r.xmrwallet.model.TransactionInfo;
import com.m2049r.xmrwallet.model.Transfer;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.service.MoneroHandlerThread;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

View File

@@ -16,16 +16,20 @@
package com.m2049r.xmrwallet;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.net.UrlQuerySanitizer;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PowerManager;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
@@ -39,10 +43,17 @@ import com.m2049r.xmrwallet.model.TransactionInfo;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.service.WalletService;
import com.m2049r.xmrwallet.util.BarcodeData;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.TxData;
import java.util.HashMap;
import java.util.Map;
public class WalletActivity extends AppCompatActivity implements WalletFragment.Listener,
WalletService.Observer, SendFragment.Listener, TxFragment.Listener, GenerateReviewFragment.ListenerWithWallet {
WalletService.Observer, SendFragment.Listener, TxFragment.Listener,
GenerateReviewFragment.ListenerWithWallet,
ScannerFragment.Listener {
private static final String TAG = "WalletActivity";
public static final String REQUEST_ID = "id";
@@ -149,7 +160,7 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
}
Fragment walletFragment = new WalletFragment();
getFragmentManager().beginTransaction()
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, walletFragment).commit();
Log.d(TAG, "fragment added");
@@ -330,7 +341,7 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
Log.d(TAG, "onRefreshed()");
try {
final WalletFragment walletFragment = (WalletFragment)
getFragmentManager().findFragmentById(R.id.fragment_container);
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if (wallet.isSynchronized()) {
Log.d(TAG, "onRefreshed() synced");
releaseWakeLock(); // the idea is to stay awake until synced
@@ -395,7 +406,7 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
public void onCreatedTransaction(final PendingTransaction pendingTransaction) {
try {
final SendFragment sendFragment = (SendFragment)
getFragmentManager().findFragmentById(R.id.fragment_container);
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
runOnUiThread(new Runnable() {
public void run() {
PendingTransaction.Status status = pendingTransaction.getStatus();
@@ -434,7 +445,7 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
public void onSetNotes(final boolean success) {
try {
final TxFragment txFragment = (TxFragment)
getFragmentManager().findFragmentById(R.id.fragment_container);
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
runOnUiThread(new Runnable() {
public void run() {
if (!success) {
@@ -454,7 +465,7 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
public void onProgress(final String text) {
try {
final WalletFragment walletFragment = (WalletFragment)
getFragmentManager().findFragmentById(R.id.fragment_container);
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
runOnUiThread(new Runnable() {
public void run() {
walletFragment.onProgress(text);
@@ -471,7 +482,7 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
public void onProgress(final int n) {
try {
final WalletFragment walletFragment = (WalletFragment)
getFragmentManager().findFragmentById(R.id.fragment_container);
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
runOnUiThread(new Runnable() {
public void run() {
walletFragment.onProgress(n);
@@ -557,7 +568,7 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
@Override
public boolean isPaymentIdValid(String paymentId) {
return getWallet().isPaymentIdValid(paymentId);
return Wallet.isPaymentIdValid(paymentId);
}
@Override
@@ -567,9 +578,9 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
void popFragmentStack(String name) {
if (name == null) {
getFragmentManager().popBackStack();
getSupportFragmentManager().popBackStack();
} else {
getFragmentManager().popBackStack(name, FragmentManager.POP_BACK_STACK_INCLUSIVE);
getSupportFragmentManager().popBackStack(name, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
@@ -577,14 +588,14 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
if (extras != null) {
newFragment.setArguments(extras);
}
FragmentTransaction transaction = getFragmentManager().beginTransaction();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(stackName);
transaction.commit();
}
private void onWalletDetails() {
Fragment fragment = getFragmentManager().findFragmentById(R.id.fragment_container);
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if (!(fragment instanceof GenerateReviewFragment)) {
Bundle extras = new Bundle();
extras.putString("type", GenerateReviewFragment.VIEW_WALLET);
@@ -596,4 +607,110 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
public void onDisposeRequest() {
getWallet().disposePendingTransaction();
}
private void startScanFragment() {
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if (fragment instanceof SendFragment) {
Bundle extras = new Bundle();
replaceFragment(new ScannerFragment(), null, extras);
}
}
/// QR scanner callbacks
@Override
public void onScanAddress() {
if (Helper.getCameraPermission(this)) {
startScanFragment();
} else {
Log.i(TAG, "Waiting for permissions");
}
}
private BarcodeData scannedData = null;
@Override
public boolean onAddressScanned(String uri) {
BarcodeData bcData = parseMoneroUri(uri);
if (bcData != null) {
this.scannedData = bcData;
popFragmentStack(null);
return true;
} else {
return false;
}
}
/**
* Parse and decode a monero scheme string. It is here because it needs to validate the data.
*
* @param uri String containing a monero URL
* @return BarcodeData object or null if uri not valid
*/
public BarcodeData parseMoneroUri(String uri) {
if (uri == null) return null;
if (!uri.startsWith(ScannerFragment.QR_SCHEME)) return null;
String noScheme = uri.substring(ScannerFragment.QR_SCHEME.length());
Uri monero = Uri.parse(noScheme);
Map<String, String> parms = new HashMap<>();
String query = monero.getQuery();
if (query != null) {
String[] args = query.split("&");
for (String arg : args) {
String[] namevalue = arg.split("=");
if (namevalue.length == 0) {
continue;
}
parms.put(Uri.decode(namevalue[0]).toLowerCase(),
namevalue.length > 1 ? Uri.decode(namevalue[1]) : null);
}
}
String address = monero.getPath();
String paymentId = parms.get(ScannerFragment.QR_PAYMENTID);
String amountString = parms.get(ScannerFragment.QR_AMOUNT);
long amount = -1;
if (amountString != null) {
amount = Wallet.getAmountFromString(amountString);
}
if ((paymentId != null) && !Wallet.isPaymentIdValid(paymentId)) {
address = null;
}
if (Helper.isAddressOk(address, WalletManager.getInstance().isTestNet())) {
return new BarcodeData(address, paymentId, amount);
}
return null;
}
@Override
public BarcodeData getScannedData() {
BarcodeData data = scannedData;
scannedData = null;
return data;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
@NonNull int[] grantResults) {
Log.d(TAG, "onRequestPermissionsResult()");
switch (requestCode) {
case Helper.PERMISSIONS_REQUEST_CAMERA:
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startScanFragment();
} else {
String msg = getString(R.string.message_camera_not_permitted);
Log.e(TAG, msg);
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
}
break;
default:
}
}
}

View File

@@ -16,21 +16,16 @@
package com.m2049r.xmrwallet;
import android.app.Fragment;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.constraint.ConstraintLayout;
import android.support.v7.app.AlertDialog;
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.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
@@ -38,9 +33,8 @@ import android.widget.TextView;
import com.m2049r.xmrwallet.layout.TransactionInfoAdapter;
import com.m2049r.xmrwallet.model.TransactionInfo;
import com.m2049r.xmrwallet.model.Transfer;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.Helper;
import java.text.NumberFormat;
import java.util.List;
@@ -69,9 +63,9 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
clProgress = (ConstraintLayout) view.findViewById(R.id.clProgress);
llUnconfirmedAmount = (LinearLayout) view.findViewById(R.id.llUnconfirmedAmount);
tvBalance = (TextView) view.findViewById(R.id.tvBalance);
tvBalance.setText(getDisplayAmount(0));
tvBalance.setText(Helper.getDisplayAmount(0));
tvUnconfirmedAmount = (TextView) view.findViewById(R.id.tvUnconfirmedAmount);
tvUnconfirmedAmount.setText(getDisplayAmount(0));
tvUnconfirmedAmount.setText(Helper.getDisplayAmount(0));
tvBlockHeightProgress = (TextView) view.findViewById(R.id.tvBlockHeightProgress);
bSend = (Button) view.findViewById(R.id.bSend);
@@ -175,23 +169,6 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
private long firstBlock = 0;
private String walletTitle = null;
private String getDisplayAmount(long amount) {
String s = Wallet.getDisplayAmount(amount);
int lastZero = 0;
int decimal = 0;
for (int i = s.length() - 1; i >= 0; i--) {
if ((lastZero == 0) && (s.charAt(i) != '0')) lastZero = i + 1;
// TODO i18n
if (s.charAt(i) == '.') {
decimal = i;
break;
}
}
//Log.d(TAG, decimal + "/" + lastZero + "/" + s);
int cutoff = Math.max(lastZero, decimal + 2);
return s.substring(0, cutoff);
}
private void updateStatus(Wallet wallet) {
Log.d(TAG, "updateStatus()");
if (walletTitle == null) {
@@ -200,8 +177,8 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
}
long balance = wallet.getBalance();
long unlockedBalance = wallet.getUnlockedBalance();
tvBalance.setText(getDisplayAmount(unlockedBalance));
tvUnconfirmedAmount.setText(getDisplayAmount(balance - unlockedBalance));
tvBalance.setText(Helper.getDisplayAmount(unlockedBalance));
tvUnconfirmedAmount.setText(Helper.getDisplayAmount(balance - unlockedBalance));
// balance cannot be less than unlockedBalance
/*if (balance != unlockedBalance) {
llPendingAmount.setVisibility(View.VISIBLE);

View File

@@ -19,10 +19,7 @@ package com.m2049r.xmrwallet.service;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -30,13 +27,11 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.WalletActivity;
import com.m2049r.xmrwallet.model.PendingTransaction;
import com.m2049r.xmrwallet.model.TransactionHistory;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletListener;
import com.m2049r.xmrwallet.model.WalletManager;
@@ -44,6 +39,8 @@ import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.TxData;
public class WalletService extends Service {
public static boolean Running = false;
final static String TAG = "WalletService";
final static int NOTIFICATION_ID = 2049;
@@ -127,6 +124,7 @@ public class WalletService extends Service {
boolean fullRefresh = false;
updateDaemonState(wallet, wallet.isSynchronized() ? height : 0);
if (!wallet.isSynchronized()) {
updated = true;
// we want to see our transactions as they come in
wallet.getHistory().refresh();
int txCount = wallet.getHistory().getCount();
@@ -399,9 +397,6 @@ public class WalletService extends Service {
@Override
public void onCreate() {
//mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
//showNotification();
// We are using a HandlerThread and a Looper to avoid loading and closing
// concurrency
MoneroHandlerThread thread = new MoneroHandlerThread("WalletService",
@@ -418,8 +413,6 @@ public class WalletService extends Service {
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy()");
// Cancel the persistent notification.
//mNM.cancel(NOTIFICATION);
if (this.listener != null) {
Log.w(TAG, "onDestroy() with active listener");
// no need to stop() here because the wallet closing should have been triggered
@@ -437,7 +430,8 @@ public class WalletService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// when the activity satrts the service, it expects to start it for a new wallet
Running = true;
// when the activity starts the service, it expects to start it for a new wallet
// the service is possibly still occupied with saving the last opened wallet
// so we queue the open request
// this should not matter since the old activity is not getting updates
@@ -513,6 +507,7 @@ public class WalletService extends Service {
}
stopForeground(true);
stopSelf();
Running = false;
}
private Wallet loadWallet(String walletName, String walletPassword) {

View File

@@ -0,0 +1,29 @@
/*
* 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.util;
public class BarcodeData {
public String address = null;
public String paymentId = null;
public long amount = -1;
public BarcodeData(String address, String paymentId, long amount) {
this.address = address;
this.paymentId = paymentId;
this.amount = amount;
}
}

View File

@@ -27,6 +27,8 @@ import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import java.io.File;
@@ -71,6 +73,24 @@ public class Helper {
}
}
public static final int PERMISSIONS_REQUEST_CAMERA = 1;
static public boolean getCameraPermission(Activity context) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
if (context.checkSelfPermission(Manifest.permission.CAMERA)
== PackageManager.PERMISSION_DENIED) {
Log.d(TAG, "Permission denied for CAMERA - requesting it");
String[] permissions = {Manifest.permission.CAMERA};
context.requestPermissions(permissions, PERMISSIONS_REQUEST_CAMERA);
return false;
} else {
return true;
}
} else {
return true;
}
}
static public String getWalletPath(Context context, String aWalletName) {
File walletDir = getStorageRoot(context);
//d(TAG, "walletdir=" + walletDir.getAbsolutePath());
@@ -107,4 +127,29 @@ public class Helper {
static public void hideKeyboardAlways(Activity act) {
act.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
}
static public boolean isAddressOk(String address, boolean testnet) {
if (address == null) return false;
if (testnet) {
return ((address.length() == 95) && ("9A".indexOf(address.charAt(0)) >= 0));
} else {
return ((address.length() == 95) && ("4".indexOf(address.charAt(0)) >= 0));
}
}
static public String getDisplayAmount(long amount) {
String s = Wallet.getDisplayAmount(amount);
int lastZero = 0;
int decimal = 0;
for (int i = s.length() - 1; i >= 0; i--) {
if ((lastZero == 0) && (s.charAt(i) != '0')) lastZero = i + 1;
// TODO i18n
if (s.charAt(i) == '.') {
decimal = i;
break;
}
}
//Log.d(TAG, decimal + "/" + lastZero + "/" + s);
int cutoff = Math.max(lastZero, decimal + 2);
return s.substring(0, cutoff);
}
}

View File

@@ -16,7 +16,7 @@
<Spinner
android:id="@+id/sMixin"
android:layout_width="0dp"
android:layout_width="0sp"
android:layout_height="match_parent"
android:layout_weight="1"
android:entries="@array/mixin"
@@ -24,7 +24,7 @@
<Spinner
android:id="@+id/sPriority"
android:layout_width="0dp"
android:layout_width="0sp"
android:layout_height="match_parent"
android:layout_weight="1"
android:entries="@array/priority"
@@ -32,25 +32,49 @@
</LinearLayout>
<EditText
android:id="@+id/etAddress"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/send_address_hint"
android:imeOptions="actionNext"
android:inputType="textMultiLine"
android:textAlignment="center"
android:textSize="16sp" />
android:orientation="horizontal"
android:layout_marginBottom="4sp"
android:layout_marginTop="4sp"
android:weightSum="10">
<EditText
android:id="@+id/etAddress"
android:layout_width="0sp"
android:layout_height="wrap_content"
android:layout_weight="8"
android:hint="@string/send_address_hint"
android:imeOptions="actionNext"
android:inputType="textMultiLine"
android:textAlignment="center"
android:textSize="16sp" />
<Button
android:id="@+id/bAddress"
android:layout_width="0sp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:minHeight="36sp"
android:layout_weight="2"
android:background="@color/colorPrimary"
android:enabled="true"
android:text="@string/send_qr_hint"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="4sp"
android:layout_marginTop="4sp"
android:weightSum="10">
<EditText
android:id="@+id/etPaymentId"
android:layout_width="0dp"
android:layout_width="0sp"
android:layout_height="wrap_content"
android:layout_weight="8"
android:hint="@string/send_paymentid_hint"
@@ -61,10 +85,10 @@
<Button
android:id="@+id/bPaymentId"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:layout_width="0sp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:minHeight="36sp"
android:layout_weight="2"
android:background="@color/colorPrimary"
android:enabled="true"
@@ -76,11 +100,13 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="4sp"
android:layout_marginTop="4sp"
android:weightSum="10">
<EditText
android:id="@+id/etAmount"
android:layout_width="0dp"
android:layout_width="0sp"
android:layout_height="wrap_content"
android:layout_weight="8"
android:hint="@string/send_amount_hint"
@@ -91,10 +117,8 @@
<Button
android:id="@+id/bSweep"
android:layout_width="0dp"
android:layout_width="0sp"
android:layout_height="match_parent"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:layout_weight="2"
android:background="@color/colorPrimary"
android:enabled="true"
@@ -106,8 +130,9 @@
android:id="@+id/bPrepareSend"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="4sp"
android:layout_marginTop="4sp"
android:minHeight="36sp"
android:background="@color/colorPrimary"
android:enabled="false"
android:text="@string/send_prepare_hint" />
@@ -124,18 +149,19 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginBottom="4sp"
android:layout_marginTop="4sp"
android:visibility="gone">
<Button
android:id="@+id/bDispose"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:minHeight="36sp"
android:layout_marginBottom="4sp"
android:background="@color/colorPrimary"
android:text="@string/send_dispose_hint" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -144,9 +170,9 @@
<TextView
android:id="@+id/tvTxAmountLabel"
android:layout_width="0dp"
android:layout_width="0sp"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_marginRight="8sp"
android:layout_weight="1"
android:text="@string/send_amount_label"
android:textAlignment="textEnd"
@@ -155,9 +181,9 @@
<TextView
android:id="@+id/tvTxAmount"
android:layout_width="0dp"
android:layout_width="0sp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginLeft="8sp"
android:layout_weight="2"
android:textAlignment="textEnd"
android:textSize="20sp" />
@@ -171,9 +197,9 @@
<TextView
android:id="@+id/tvTxFeeLabel"
android:layout_width="0dp"
android:layout_width="0sp"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_marginRight="4sp"
android:layout_weight="1"
android:text="@string/send_fee_label"
android:textAlignment="textEnd"
@@ -182,9 +208,9 @@
<TextView
android:id="@+id/tvTxFee"
android:layout_width="0dp"
android:layout_width="0sp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginLeft="8sp"
android:layout_weight="2"
android:textAlignment="textEnd"
android:textSize="20sp" />
@@ -198,9 +224,9 @@
<TextView
android:id="@+id/tvTxDustLabel"
android:layout_width="0dp"
android:layout_width="0sp"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_marginRight="8sp"
android:layout_weight="1"
android:text="@string/send_dust_label"
android:textAlignment="textEnd"
@@ -209,9 +235,9 @@
<TextView
android:id="@+id/tvTxDust"
android:layout_width="0dp"
android:layout_width="0sp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginLeft="8sp"
android:layout_weight="2"
android:textAlignment="textEnd"
android:textSize="20sp" />
@@ -221,7 +247,7 @@
android:id="@+id/etNotes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginTop="4sp"
android:hint="@string/send_notes_hint"
android:imeOptions="actionDone"
android:inputType="textMultiLine"
@@ -232,8 +258,9 @@
android:id="@+id/bSend"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="4sp"
android:layout_marginTop="4sp"
android:minHeight="36sp"
android:background="@color/colorPrimary"
android:text="@string/send_send_hint" />
@@ -241,8 +268,8 @@
android:id="@+id/bReallySend"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="4sp"
android:layout_marginTop="4sp"
android:background="@color/moneroOrange"
android:text="@string/send_really_send_hint"
android:visibility="gone" />

View File

@@ -5,7 +5,7 @@
<string name="menu_info">Details</string>
<string name="prompt_daemon">[&lt;user&gt;:&lt;pass&gt;@]&lt;daemonhost&gt;[:&lt;port&gt;]</string>
<string name="prompt_daemon">[&lt;user&gt;:&lt;pass&gt;@]&lt;daemon&gt;[:&lt;port&gt;]</string>
<string name="prompt_mainnet">Net Selection</string>
<string name="connect_testnet">TestNet</string>
<string name="connect_mainnet">MainNet</string>
@@ -22,6 +22,8 @@
<string name="status_transaction_failed">Transaction failed!</string>
<string name="status_transaction_prepare_failed">Could not create transaction!</string>
<string name="service_busy">I am still busy with your last wallet &#8230;</string>
<string name="prompt_password">Password for</string>
<string name="bad_password">Bad password!</string>
<string name="bad_wallet">Wallet does not exists!</string>
@@ -58,6 +60,7 @@
<string name="prompt_problems">Problems</string>
<string name="message_strorage_not_writable">External Storage is not writable! Panic!</string>
<string name="message_strorage_not_permitted">We really need those External Storage permissions!</string>
<string name="message_camera_not_permitted">No camera = No QR scanning!</string>
<string name="generate_title">Create Wallet</string>
<string name="generate_name_hint">Wallet Name</string>
@@ -111,11 +114,13 @@
<string name="send_mixin_hint">Mixin</string>
<string name="send_sweep_hint">Sweep</string>
<string name="send_generate_paymentid_hint">Generate</string>
<string name="send_qr_hint">Scan</string>
<string name="send_prepare_hint">Prepare</string>
<string name="send_dispose_hint">Dispose (Undo)</string>
<string name="send_send_hint">Spend my sweet Moneroj</string>
<string name="send_really_send_hint">I understand I am on mainnet\nTHis is real Monero!</string>
<string name="send_really_send_hint">I understand I am sending real Monero!</string>
<string name="send_qr_invalid">Not a monero QR Code</string>
<string name="send_qr_address_invalid">Invalid Monero address</string>
<string name="send_preparing_progress">Preparing transaction</string>
<string name="send_amount_label">Amount</string>

View File

@@ -2,6 +2,7 @@
## What features does it have?
- Support for Android >= 5.0 with ARM processor
- Testnet and Mainnet
- Generate new wallets
- Recover wallets form mnemonic seed or from keys
@@ -14,7 +15,8 @@
- Background updating (make sure you exit the wallet to stop updating to save some battery)
- Access to daemon with username/password and nonstandard port
- Only 5 decimal places shown in transactions (full amount in details - click on transaction)
- All significant figures shown balance
- All significant figures shown in balance
- QR Code scanning - make sure to *ALWAYS* verify the scanned code is what it is advertised to be!
## I cannot select and copy the mnemonic seed
Copying anything to the clipboard on Android exposes it to any other App running. So this

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 KiB