mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-03 08:23:04 +02:00
Compare commits
33 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
5b38cc438c | ||
![]() |
1eac363102 | ||
![]() |
e51425bc2e | ||
![]() |
f5535dbefa | ||
![]() |
2d42a0287d | ||
![]() |
2ebb9d0650 | ||
![]() |
30792c53c0 | ||
![]() |
3846a0c391 | ||
![]() |
c66ebd654a | ||
![]() |
186ed5cd39 | ||
![]() |
e39cd1c988 | ||
![]() |
39d9c4d0c3 | ||
![]() |
e4562f07a4 | ||
![]() |
b3ea4540a1 | ||
![]() |
ea7c7d2fdb | ||
![]() |
8ff8be6f60 | ||
![]() |
b5ded700fe | ||
![]() |
a5527d4efd | ||
![]() |
a007810824 | ||
![]() |
a634cf18b1 | ||
![]() |
0b2fa08df8 | ||
![]() |
191aa6ac22 | ||
![]() |
a82a90575d | ||
![]() |
2e04046f50 | ||
![]() |
c11d577c5f | ||
![]() |
74b2dd209c | ||
![]() |
cb1d541474 | ||
![]() |
807d217aac | ||
![]() |
0421bcfac3 | ||
![]() |
b6c4e06fda | ||
![]() |
3ee8343074 | ||
![]() |
110621824a | ||
![]() |
7194580fbe |
10
.idea/libraries/core_1_9_8.xml
generated
Normal file
10
.idea/libraries/core_1_9_8.xml
generated
Normal 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
11
.idea/libraries/core_3_3_0.xml
generated
Normal 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
10
.idea/libraries/zxing_1_9_8.xml
generated
Normal 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>
|
15
README.md
15
README.md
@@ -8,12 +8,11 @@ Another Android Monero Wallet
|
||||
- see the [FAQ](doc/FAQ.md)
|
||||
|
||||
### Disclaimer
|
||||
You may loose all your Moneroj if you use this App.
|
||||
You may loose all your Moneroj if you use this App. Be cautious when spending on mainnet.
|
||||
|
||||
### Random Notes
|
||||
- Based off monero v0.10.3.1 with pull requests #2238, #2239 and #2289 applied => so can be used in mainnet!
|
||||
- currently only android32
|
||||
- ~~currently only use is checking incoming/outgoing transactions~~
|
||||
- currently only android32 (runs on 64-bit as well)
|
||||
- works in testnet & mainnet
|
||||
- takes forever to sync due to 32-bit architecture
|
||||
- use your own daemon - it's easy
|
||||
@@ -23,15 +22,12 @@ You may loose all your Moneroj if you use this App.
|
||||
|
||||
### TODO
|
||||
- wallet backup functions
|
||||
- adjust layout so we can use bigger font sizes (maybe show only 5 decimal places instead of 12 in main view)
|
||||
- review visibility of methods/classes
|
||||
- more sensible error dialogs ~~(e.g. when no write permissions granted) instead of just crashing on purpose~~
|
||||
- more sensible error dialogs
|
||||
- check licenses of included libraries; License Dialog
|
||||
- ~~make it pretty~~ (decided to go with "form follows function")
|
||||
- ~~spend monero - not so difficult with wallet api~~
|
||||
|
||||
### Issues
|
||||
- Pending incoming transactions disappear after reload
|
||||
- Pending incoming transactions disappear after reload (and appear after being mined)
|
||||
|
||||
### HOW TO BUILD
|
||||
No need to build. Binaries are included:
|
||||
@@ -45,4 +41,5 @@ If you want to build them yourself (recommended) check out [the instructions](do
|
||||
Then, fire up Android Studio and build the APK.
|
||||
|
||||
### Donations
|
||||
4AdkPJoxn7JCvAby9szgnt93MSEwdnxdhaASxbTBm6x5dCwmsDep2UYN4FhStDn5i11nsJbpU7oj59ahg8gXb1Mg3viqCuk
|
||||
- Address: 4AdkPJoxn7JCvAby9szgnt93MSEwdnxdhaASxbTBm6x5dCwmsDep2UYN4FhStDn5i11nsJbpU7oj59ahg8gXb1Mg3viqCuk
|
||||
- Viewkey: b1aff2a12191723da0afbe75516f94dd8b068215f6e847d8da57aca5f1f98e0c
|
@@ -7,8 +7,8 @@ android {
|
||||
applicationId "com.m2049r.xmrwallet"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 25
|
||||
versionCode 5
|
||||
versionName "0.4.1"
|
||||
versionCode 8
|
||||
versionName "0.5.1"
|
||||
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'
|
||||
}
|
||||
|
@@ -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"
|
||||
|
@@ -592,12 +592,16 @@ Java_com_m2049r_xmrwallet_model_Wallet_getFilename(JNIEnv *env, jobject instance
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_m2049r_xmrwallet_model_Wallet_initJ(JNIEnv *env, jobject instance,
|
||||
jstring daemon_address,
|
||||
jlong upper_transaction_size_limit) {
|
||||
// const std::string &daemon_username = "", const std::string &daemon_password = "") = 0;
|
||||
jlong upper_transaction_size_limit,
|
||||
jstring daemon_username, jstring daemon_password) {
|
||||
const char *_daemon_address = env->GetStringUTFChars(daemon_address, JNI_FALSE);
|
||||
const char *_daemon_username = env->GetStringUTFChars(daemon_username, JNI_FALSE);
|
||||
const char *_daemon_password = env->GetStringUTFChars(daemon_password, JNI_FALSE);
|
||||
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
|
||||
bool status = wallet->init(_daemon_address, upper_transaction_size_limit);
|
||||
bool status = wallet->init(_daemon_address, upper_transaction_size_limit, _daemon_username, _daemon_password);
|
||||
env->ReleaseStringUTFChars(daemon_address, _daemon_address);
|
||||
env->ReleaseStringUTFChars(daemon_username, _daemon_username);
|
||||
env->ReleaseStringUTFChars(daemon_password, _daemon_password);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@@ -22,6 +22,7 @@ import android.os.Bundle;
|
||||
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;
|
||||
@@ -344,6 +345,13 @@ public class GenerateFragment extends Fragment {
|
||||
bGenerate.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
Log.d(TAG, "onPause()");
|
||||
activityCallback.setTitle(getString(R.string.generate_title));
|
||||
}
|
||||
|
||||
GenerateFragment.Listener activityCallback;
|
||||
|
||||
public interface Listener {
|
||||
@@ -354,6 +362,9 @@ public class GenerateFragment extends Fragment {
|
||||
void onGenerate(String name, String password, String address, String viewKey, String spendKey, long height);
|
||||
|
||||
File getStorageRoot();
|
||||
|
||||
void setTitle(String title);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -65,6 +65,7 @@ public class GenerateReviewFragment extends Fragment {
|
||||
|
||||
boolean testnet = WalletManager.getInstance().isTestNet();
|
||||
tvWalletMnemonic.setTextIsSelectable(testnet);
|
||||
tvWalletSpendKey.setTextIsSelectable(testnet);
|
||||
|
||||
bAccept.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
|
@@ -55,6 +55,8 @@ public class LoginActivity extends AppCompatActivity
|
||||
|
||||
static final int DAEMON_TIMEOUT = 500; // deamon must respond in 500ms
|
||||
|
||||
Toolbar toolbar;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -63,8 +65,7 @@ public class LoginActivity extends AppCompatActivity
|
||||
return;
|
||||
}
|
||||
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.tbLogin);
|
||||
toolbar.setTitle(R.string.login_activity_name);
|
||||
toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
if (Helper.getWritePermission(this)) {
|
||||
@@ -77,21 +78,17 @@ public class LoginActivity extends AppCompatActivity
|
||||
@Override
|
||||
public void onWalletSelected(final String walletName) {
|
||||
Log.d(TAG, "selected wallet is ." + walletName + ".");
|
||||
if (walletName.equals(':' + getString(R.string.generate_title))) {
|
||||
startGenerateFragment();
|
||||
} else {
|
||||
// now it's getting real, check if wallet exists
|
||||
String walletPath = Helper.getWalletPath(this, walletName);
|
||||
if (WalletManager.getInstance().walletExists(walletPath)) {
|
||||
promptPassword(walletName, new PasswordAction() {
|
||||
@Override
|
||||
public void action(String walletName, String password) {
|
||||
startWallet(walletName, password);
|
||||
}
|
||||
});
|
||||
} else { // this cannot really happen as we prefilter choices
|
||||
Toast.makeText(this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
// now it's getting real, check if wallet exists
|
||||
String walletPath = Helper.getWalletPath(this, walletName);
|
||||
if (WalletManager.getInstance().walletExists(walletPath)) {
|
||||
promptPassword(walletName, new PasswordAction() {
|
||||
@Override
|
||||
public void action(String walletName, String password) {
|
||||
startWallet(walletName, password);
|
||||
}
|
||||
});
|
||||
} else { // this cannot really happen as we prefilter choices
|
||||
Toast.makeText(this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +108,11 @@ public class LoginActivity extends AppCompatActivity
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAddWallet() {
|
||||
startGenerateFragment();
|
||||
}
|
||||
|
||||
AlertDialog passwordDialog = null; // for preventing multiple clicks in wallet list
|
||||
|
||||
void promptPassword(final String wallet, final PasswordAction action) {
|
||||
@@ -203,9 +205,22 @@ public class LoginActivity extends AppCompatActivity
|
||||
|
||||
@Override
|
||||
public void setTitle(String title) {
|
||||
super.setTitle(title);
|
||||
toolbar.setTitle(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSubtitle(String subtitle) {
|
||||
toolbar.setSubtitle(subtitle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTestNet(boolean testnet) {
|
||||
if (testnet) {
|
||||
toolbar.setBackgroundResource(R.color.colorPrimaryDark);
|
||||
} else {
|
||||
toolbar.setBackgroundResource(R.color.moneroOrange);
|
||||
}
|
||||
}
|
||||
////////////////////////////////////////
|
||||
////////////////////////////////////////
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
131
app/src/main/java/com/m2049r/xmrwallet/ScannerFragment.java
Normal file
131
app/src/main/java/com/m2049r/xmrwallet/ScannerFragment.java
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
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.model.WalletManager;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
|
||||
import me.dm7.barcodescanner.zxing.ZXingScannerView;
|
||||
|
||||
public class ScannerFragment extends Fragment implements ZXingScannerView.ResultHandler {
|
||||
static final String TAG = "ScannerFragment";
|
||||
|
||||
Listener activityCallback;
|
||||
|
||||
public interface Listener {
|
||||
void onAddressScanned(String address, String paymentId);
|
||||
|
||||
boolean isPaymentIdValid(String paymentId);
|
||||
}
|
||||
|
||||
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 URI_PREFIX = "monero:";
|
||||
static final String PAYMENTID_STRING = "?tx_payment_id=";
|
||||
|
||||
@Override
|
||||
public void handleResult(Result rawResult) {
|
||||
Log.d(TAG, rawResult.getBarcodeFormat().toString() + "/" + rawResult.getText());
|
||||
String text = rawResult.getText();
|
||||
if ((rawResult.getBarcodeFormat() == BarcodeFormat.QR_CODE) &&
|
||||
(text.startsWith(URI_PREFIX))) {
|
||||
String address = null;
|
||||
String paymentId = null;
|
||||
String s = text.substring(URI_PREFIX.length());
|
||||
if (s.length() == 95) {
|
||||
address = s;
|
||||
} else {
|
||||
int i = s.indexOf(PAYMENTID_STRING);
|
||||
if ((i == 95) && (s.length() == (95 + PAYMENTID_STRING.length() + 16))) {
|
||||
address = s.substring(0, 95);
|
||||
paymentId = s.substring(95 + PAYMENTID_STRING.length());
|
||||
if (!activityCallback.isPaymentIdValid(paymentId)) {
|
||||
address = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Helper.isAddressOk(address, WalletManager.getInstance().isTestNet())) {
|
||||
activityCallback.onAddressScanned(address, paymentId);
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
@@ -17,11 +17,10 @@
|
||||
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.os.Handler;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
@@ -40,23 +39,24 @@ import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.m2049r.xmrwallet.model.PendingTransaction;
|
||||
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 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;
|
||||
Button bPrepareSend;
|
||||
Button bDispose;
|
||||
Button bPaymentId;
|
||||
LinearLayout llConfirmSend;
|
||||
TextView tvTxAmount;
|
||||
@@ -64,6 +64,7 @@ public class SendFragment extends Fragment {
|
||||
TextView tvTxDust;
|
||||
EditText etNotes;
|
||||
Button bSend;
|
||||
Button bReallySend;
|
||||
ProgressBar pbProgress;
|
||||
|
||||
final static int Mixins[] = {4, 6, 8, 10, 13}; // must match the layout XML
|
||||
@@ -83,9 +84,11 @@ 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);
|
||||
bDispose = (Button) view.findViewById(R.id.bDispose);
|
||||
|
||||
llConfirmSend = (LinearLayout) view.findViewById(R.id.llConfirmSend);
|
||||
tvTxAmount = (TextView) view.findViewById(R.id.tvTxAmount);
|
||||
@@ -93,6 +96,7 @@ public class SendFragment extends Fragment {
|
||||
tvTxDust = (TextView) view.findViewById(R.id.tvTxDust);
|
||||
etNotes = (EditText) view.findViewById(R.id.etNotes);
|
||||
bSend = (Button) view.findViewById(R.id.bSend);
|
||||
bReallySend = (Button) view.findViewById(R.id.bReallySend);
|
||||
|
||||
pbProgress = (ProgressBar) view.findViewById(R.id.pbProgress);
|
||||
|
||||
@@ -100,10 +104,6 @@ public class SendFragment extends Fragment {
|
||||
etPaymentId.setRawInputType(InputType.TYPE_CLASS_TEXT);
|
||||
etNotes.setRawInputType(InputType.TYPE_CLASS_TEXT);
|
||||
|
||||
// etAddress.setText("9tDC52GsMjTNt4dpnRCwAF7ekVBkbkgkXGaMKTcSTpBhGpqkPX56jCNRydLq9oGjbbAQBsZhLfgmTKsntmxRd3TaJFYM2f8");
|
||||
boolean testnet = WalletManager.getInstance().isTestNet();
|
||||
if (!testnet) throw new IllegalStateException("Sending TX only on testnet. sorry.");
|
||||
|
||||
Helper.showKeyboard(getActivity());
|
||||
etAddress.requestFocus();
|
||||
etAddress.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
@@ -191,6 +191,21 @@ public class SendFragment extends Fragment {
|
||||
}
|
||||
});
|
||||
|
||||
bDispose.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
activityCallback.onDisposeRequest();
|
||||
enableEdit();
|
||||
}
|
||||
});
|
||||
|
||||
bAddress.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
activityCallback.onScanAddress();
|
||||
}
|
||||
});
|
||||
|
||||
bPaymentId.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@@ -224,6 +239,26 @@ public class SendFragment extends Fragment {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
bSend.setEnabled(false);
|
||||
boolean testnet = WalletManager.getInstance().isTestNet();
|
||||
if (testnet) {
|
||||
send();
|
||||
} else {
|
||||
etNotes.setEnabled(false);
|
||||
Handler handler = new Handler();
|
||||
handler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
bReallySend.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
bReallySend.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
bReallySend.setEnabled(false);
|
||||
send();
|
||||
}
|
||||
});
|
||||
@@ -240,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() {
|
||||
@@ -304,6 +335,10 @@ public class SendFragment extends Fragment {
|
||||
bPaymentId.setEnabled(true);
|
||||
bSweep.setEnabled(true);
|
||||
bPrepareSend.setEnabled(true);
|
||||
llConfirmSend.setVisibility(View.GONE);
|
||||
bSend.setEnabled(true);
|
||||
etNotes.setEnabled(true);
|
||||
bReallySend.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void send() {
|
||||
@@ -327,6 +362,37 @@ public class SendFragment extends Fragment {
|
||||
|
||||
String getWalletAddress();
|
||||
|
||||
void onDisposeRequest();
|
||||
|
||||
void onScanAddress();
|
||||
|
||||
BarcodeData getScannedData();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
BarcodeData data = activityCallback.getScannedData();
|
||||
if (data != null) {
|
||||
String scannedAddress = data.address;
|
||||
if (scannedAddress != null) {
|
||||
etAddress.setText(scannedAddress);
|
||||
}
|
||||
String scannedPaymenId = data.paymentId;
|
||||
if (scannedPaymenId != null) {
|
||||
etPaymentId.setText(scannedPaymenId);
|
||||
}
|
||||
}
|
||||
// jump to first empty field
|
||||
if (etAddress.getText().toString().isEmpty()) {
|
||||
etAddress.requestFocus();
|
||||
} else if (etPaymentId.getText().toString().isEmpty()) {
|
||||
etPaymentId.requestFocus();
|
||||
} else {
|
||||
etAmount.requestFocus();
|
||||
}
|
||||
Log.d(TAG, "onResume");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -350,7 +416,6 @@ public class SendFragment extends Fragment {
|
||||
tvTxAmount.setText(Wallet.getDisplayAmount(pendingTransaction.getAmount()));
|
||||
tvTxFee.setText(Wallet.getDisplayAmount(pendingTransaction.getFee()));
|
||||
tvTxDust.setText(Wallet.getDisplayAmount(pendingTransaction.getDust()));
|
||||
bSend.setEnabled(true);
|
||||
}
|
||||
|
||||
public void onCreatedTransactionFailed(String errorText) {
|
||||
|
@@ -41,6 +41,8 @@ import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class TxFragment extends Fragment {
|
||||
@@ -48,7 +50,7 @@ public class TxFragment extends Fragment {
|
||||
|
||||
static public final String ARG_INFO = "info";
|
||||
|
||||
private final SimpleDateFormat TS_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
private final SimpleDateFormat TS_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
|
||||
|
||||
public TxFragment() {
|
||||
super();
|
||||
@@ -60,6 +62,7 @@ public class TxFragment extends Fragment {
|
||||
TextView tvTxTimestamp;
|
||||
TextView tvTxId;
|
||||
TextView tvTxKey;
|
||||
TextView tvDestination;
|
||||
TextView tvTxPaymentId;
|
||||
TextView tvTxBlockheight;
|
||||
TextView tvTxAmount;
|
||||
@@ -78,6 +81,7 @@ public class TxFragment extends Fragment {
|
||||
tvTxTimestamp = (TextView) view.findViewById(R.id.tvTxTimestamp);
|
||||
tvTxId = (TextView) view.findViewById(R.id.tvTxId);
|
||||
tvTxKey = (TextView) view.findViewById(R.id.tvTxKey);
|
||||
tvDestination = (TextView) view.findViewById(R.id.tvDestination);
|
||||
tvTxPaymentId = (TextView) view.findViewById(R.id.tvTxPaymentId);
|
||||
tvTxBlockheight = (TextView) view.findViewById(R.id.tvTxBlockheight);
|
||||
tvTxAmount = (TextView) view.findViewById(R.id.tvTxAmount);
|
||||
@@ -142,10 +146,10 @@ public class TxFragment extends Fragment {
|
||||
sb.append(getString(R.string.tx_timestamp)).append(": ");
|
||||
sb.append(TS_FORMATTER.format(new Date(info.timestamp * 1000))).append("\n");
|
||||
sb.append(getString(R.string.tx_blockheight)).append(": ");
|
||||
if (info.isPending) {
|
||||
sb.append(getString(R.string.tx_pending)).append("\n");
|
||||
} else if (info.isFailed) {
|
||||
if (info.isFailed) {
|
||||
sb.append(getString(R.string.tx_failed)).append("\n");
|
||||
} else if (info.isPending) {
|
||||
sb.append(getString(R.string.tx_pending)).append("\n");
|
||||
} else {
|
||||
sb.append(info.blockheight).append("\n");
|
||||
}
|
||||
@@ -154,11 +158,11 @@ public class TxFragment extends Fragment {
|
||||
boolean comma = false;
|
||||
for (Transfer transfer : info.transfers) {
|
||||
if (comma) {
|
||||
sb.append(",");
|
||||
sb.append(", ");
|
||||
} else {
|
||||
comma = true;
|
||||
}
|
||||
sb.append("[").append(transfer.address.substring(0, 6)).append("] ");
|
||||
sb.append(transfer.address).append(": ");
|
||||
sb.append(Wallet.getDisplayAmount(transfer.amount));
|
||||
}
|
||||
} else {
|
||||
@@ -192,20 +196,23 @@ public class TxFragment extends Fragment {
|
||||
tvTxId.setText(info.hash);
|
||||
tvTxKey.setText(info.txKey.isEmpty() ? "-" : info.txKey);
|
||||
tvTxPaymentId.setText(info.paymentId);
|
||||
if (info.isPending) {
|
||||
tvTxBlockheight.setText(getString(R.string.tx_pending));
|
||||
} else if (info.isFailed) {
|
||||
if (info.isFailed) {
|
||||
tvTxBlockheight.setText(getString(R.string.tx_failed));
|
||||
} else if (info.isPending) {
|
||||
tvTxBlockheight.setText(getString(R.string.tx_pending));
|
||||
} else {
|
||||
tvTxBlockheight.setText("" + info.blockheight);
|
||||
}
|
||||
String sign = (info.direction == TransactionInfo.Direction.Direction_In ? "+" : "-");
|
||||
tvTxAmount.setText(sign + Wallet.getDisplayAmount(info.amount));
|
||||
tvTxFee.setText(Wallet.getDisplayAmount(info.fee));
|
||||
Set<String> destinations = new HashSet<>();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
StringBuffer dstSb = new StringBuffer();
|
||||
if (info.transfers != null) {
|
||||
boolean newline = false;
|
||||
for (Transfer transfer : info.transfers) {
|
||||
destinations.add(transfer.address);
|
||||
if (newline) {
|
||||
sb.append("\n");
|
||||
} else {
|
||||
@@ -214,10 +221,21 @@ public class TxFragment extends Fragment {
|
||||
sb.append("[").append(transfer.address.substring(0, 6)).append("] ");
|
||||
sb.append(Wallet.getDisplayAmount(transfer.amount));
|
||||
}
|
||||
newline = false;
|
||||
for (String dst : destinations) {
|
||||
if (newline) {
|
||||
dstSb.append("\n");
|
||||
} else {
|
||||
newline = true;
|
||||
}
|
||||
dstSb.append(dst);
|
||||
}
|
||||
} else {
|
||||
sb.append("-");
|
||||
dstSb.append(info.direction == TransactionInfo.Direction.Direction_In ? activityCallback.getWalletAddress() : "-");
|
||||
}
|
||||
tvTxTransfers.setText(sb.toString());
|
||||
tvDestination.setText(dstSb.toString());
|
||||
this.info = info;
|
||||
bCopy.setEnabled(true);
|
||||
}
|
||||
|
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.m2049r.xmrwallet;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.app.FragmentTransaction;
|
||||
@@ -24,13 +23,17 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.PowerManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.m2049r.xmrwallet.model.PendingTransaction;
|
||||
@@ -38,19 +41,20 @@ 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;
|
||||
|
||||
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";
|
||||
|
||||
static final int MIN_DAEMON_VERSION = 65544;
|
||||
|
||||
public static final String REQUEST_ID = "id";
|
||||
public static final String REQUEST_PW = "pw";
|
||||
|
||||
Toolbar tbWallet;
|
||||
Toolbar toolbar;
|
||||
|
||||
private boolean synced = false;
|
||||
|
||||
@@ -110,6 +114,27 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
if (!haveWallet) return true;
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.wallet_menu, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_info:
|
||||
onWalletDetails();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
Log.d(TAG, "onCreate()");
|
||||
@@ -119,15 +144,15 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
|
||||
return;
|
||||
}
|
||||
|
||||
tbWallet = (Toolbar) findViewById(R.id.tbWallet);
|
||||
tbWallet.setTitle(R.string.app_name);
|
||||
setSupportActionBar(tbWallet);
|
||||
tbWallet.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onWalletDetails();
|
||||
}
|
||||
});
|
||||
toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
toolbar.setTitle(R.string.app_name);
|
||||
setSupportActionBar(toolbar);
|
||||
boolean testnet = WalletManager.getInstance().isTestNet();
|
||||
if (testnet) {
|
||||
toolbar.setBackgroundResource(R.color.colorPrimaryDark);
|
||||
} else {
|
||||
toolbar.setBackgroundResource(R.color.moneroOrange);
|
||||
}
|
||||
|
||||
Fragment walletFragment = new WalletFragment();
|
||||
getFragmentManager().beginTransaction()
|
||||
@@ -161,6 +186,7 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
|
||||
String walletId = extras.getString(REQUEST_ID);
|
||||
if (walletId != null) {
|
||||
setTitle(walletId);
|
||||
setSubtitle("");
|
||||
}
|
||||
}
|
||||
updateProgress();
|
||||
@@ -175,6 +201,7 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
|
||||
// see this happen.
|
||||
mBoundService = null;
|
||||
setTitle(getString(R.string.wallet_activity_name));
|
||||
setSubtitle("");
|
||||
Log.d(TAG, "DISCONNECTED");
|
||||
}
|
||||
};
|
||||
@@ -270,7 +297,12 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
|
||||
|
||||
@Override
|
||||
public void setTitle(String title) {
|
||||
tbWallet.setTitle(title);
|
||||
toolbar.setTitle(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSubtitle(String subtitle) {
|
||||
toolbar.setSubtitle(subtitle);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -306,6 +338,7 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
|
||||
final WalletFragment walletFragment = (WalletFragment)
|
||||
getFragmentManager().findFragmentById(R.id.fragment_container);
|
||||
if (wallet.isSynchronized()) {
|
||||
Log.d(TAG, "onRefreshed() synced");
|
||||
releaseWakeLock(); // the idea is to stay awake until synced
|
||||
if (!synced) {
|
||||
onProgress(null);
|
||||
@@ -345,6 +378,25 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
|
||||
});
|
||||
}
|
||||
|
||||
boolean haveWallet = false;
|
||||
|
||||
@Override
|
||||
public void onWalletStarted(final boolean success) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
if (!success) {
|
||||
Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_connect_failed), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!success) {
|
||||
finish();
|
||||
} else {
|
||||
haveWallet = true;
|
||||
invalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatedTransaction(final PendingTransaction pendingTransaction) {
|
||||
try {
|
||||
@@ -539,10 +591,71 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
|
||||
|
||||
private void onWalletDetails() {
|
||||
Fragment fragment = getFragmentManager().findFragmentById(R.id.fragment_container);
|
||||
if (fragment instanceof WalletFragment) {
|
||||
if (!(fragment instanceof GenerateReviewFragment)) {
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString("type", GenerateReviewFragment.VIEW_WALLET);
|
||||
replaceFragment(new GenerateReviewFragment(), null, extras);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisposeRequest() {
|
||||
getWallet().disposePendingTransaction();
|
||||
}
|
||||
|
||||
|
||||
private void startScanFragment() {
|
||||
Fragment fragment = getFragmentManager().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 void onAddressScanned(String address, String paymentId) {
|
||||
// Log.d(TAG, "got " + address);
|
||||
scannedData = new BarcodeData(address, paymentId);
|
||||
popFragmentStack(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:
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -22,6 +22,7 @@ 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.v7.widget.DividerItemDecoration;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
@@ -29,6 +30,7 @@ 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;
|
||||
@@ -49,10 +51,10 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
|
||||
private NumberFormat formatter = NumberFormat.getInstance();
|
||||
|
||||
TextView tvBalance;
|
||||
TextView tvUnlockedBalance;
|
||||
LinearLayout llUnconfirmedAmount;
|
||||
TextView tvUnconfirmedAmount;
|
||||
TextView tvBlockHeightProgress;
|
||||
TextView tvConnectionStatus;
|
||||
LinearLayout llProgress;
|
||||
ConstraintLayout clProgress;
|
||||
TextView tvProgress;
|
||||
ProgressBar pbProgress;
|
||||
Button bSend;
|
||||
@@ -64,11 +66,13 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
|
||||
|
||||
tvProgress = (TextView) view.findViewById(R.id.tvProgress);
|
||||
pbProgress = (ProgressBar) view.findViewById(R.id.pbProgress);
|
||||
llProgress = (LinearLayout) view.findViewById(R.id.llProgress);
|
||||
clProgress = (ConstraintLayout) view.findViewById(R.id.clProgress);
|
||||
llUnconfirmedAmount = (LinearLayout) view.findViewById(R.id.llUnconfirmedAmount);
|
||||
tvBalance = (TextView) view.findViewById(R.id.tvBalance);
|
||||
tvUnlockedBalance = (TextView) view.findViewById(R.id.tvUnlockedBalance);
|
||||
tvBalance.setText(getDisplayAmount(0));
|
||||
tvUnconfirmedAmount = (TextView) view.findViewById(R.id.tvUnconfirmedAmount);
|
||||
tvUnconfirmedAmount.setText(getDisplayAmount(0));
|
||||
tvBlockHeightProgress = (TextView) view.findViewById(R.id.tvBlockHeightProgress);
|
||||
tvConnectionStatus = (TextView) view.findViewById(R.id.tvConnectionStatus);
|
||||
|
||||
bSend = (Button) view.findViewById(R.id.bSend);
|
||||
|
||||
@@ -118,7 +122,7 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
|
||||
}
|
||||
|
||||
public void onSynced() {
|
||||
if (!activityCallback.isWatchOnly() && WalletManager.getInstance().isTestNet()) {
|
||||
if (!activityCallback.isWatchOnly()) {
|
||||
bSend.setVisibility(View.VISIBLE);
|
||||
bSend.setEnabled(true);
|
||||
}
|
||||
@@ -145,11 +149,11 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
|
||||
}
|
||||
|
||||
public void showProgress() {
|
||||
llProgress.setVisibility(View.VISIBLE);
|
||||
clProgress.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
public void hideProgress() {
|
||||
llProgress.setVisibility(View.GONE);
|
||||
clProgress.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
String setActivityTitle(Wallet wallet) {
|
||||
@@ -158,10 +162,12 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
|
||||
if (shortName.length() > 16) {
|
||||
shortName = shortName.substring(0, 14) + "...";
|
||||
}
|
||||
String title = "[" + wallet.getAddress().substring(0, 6) + "] "
|
||||
+ shortName
|
||||
+ (wallet.isWatchOnly() ? " " + getString(R.string.watchonly_label) : "");
|
||||
String title = "[" + wallet.getAddress().substring(0, 6) + "] " + shortName;
|
||||
activityCallback.setTitle(title);
|
||||
|
||||
String watchOnly = (wallet.isWatchOnly() ? " " + getString(R.string.watchonly_label) : "");
|
||||
String net = (wallet.isTestNet() ? getString(R.string.connect_testnet) : getString(R.string.connect_mainnet));
|
||||
activityCallback.setSubtitle(net + " " + watchOnly);
|
||||
Log.d(TAG, "wallet title is " + title);
|
||||
return title;
|
||||
}
|
||||
@@ -169,14 +175,39 @@ 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) {
|
||||
walletTitle = setActivityTitle(wallet);
|
||||
onProgress(100); // of loading
|
||||
}
|
||||
tvBalance.setText(Wallet.getDisplayAmount(wallet.getBalance()));
|
||||
tvUnlockedBalance.setText(Wallet.getDisplayAmount(wallet.getUnlockedBalance()));
|
||||
long balance = wallet.getBalance();
|
||||
long unlockedBalance = wallet.getUnlockedBalance();
|
||||
tvBalance.setText(getDisplayAmount(unlockedBalance));
|
||||
tvUnconfirmedAmount.setText(getDisplayAmount(balance - unlockedBalance));
|
||||
// balance cannot be less than unlockedBalance
|
||||
/*if (balance != unlockedBalance) {
|
||||
llPendingAmount.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
llPendingAmount.setVisibility(View.INVISIBLE);
|
||||
}*/
|
||||
String sync = "";
|
||||
if (!activityCallback.hasBoundService())
|
||||
throw new IllegalStateException("WalletService not bound.");
|
||||
@@ -197,9 +228,10 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
|
||||
sync = getString(R.string.status_synced) + ": " + formatter.format(wallet.getBlockChainHeight());
|
||||
}
|
||||
}
|
||||
String net = (wallet.isTestNet() ? getString(R.string.connect_testnet) : getString(R.string.connect_mainnet));
|
||||
tvBlockHeightProgress.setText(sync);
|
||||
tvConnectionStatus.setText(net + " " + daemonConnected.toString().substring(17));
|
||||
//String net = (wallet.isTestNet() ? getString(R.string.connect_testnet) : getString(R.string.connect_mainnet));
|
||||
//activityCallback.setSubtitle(net + " " + daemonConnected.toString().substring(17));
|
||||
// TODO show connected status somewhere
|
||||
}
|
||||
|
||||
Listener activityCallback;
|
||||
@@ -216,6 +248,8 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
|
||||
|
||||
void setTitle(String title);
|
||||
|
||||
void setSubtitle(String subtitle);
|
||||
|
||||
void onSendRequest();
|
||||
|
||||
void onTxDetailsRequest(TransactionInfo info);
|
||||
|
@@ -40,8 +40,7 @@ import java.util.TimeZone;
|
||||
public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfoAdapter.ViewHolder> {
|
||||
private static final String TAG = "TransactionInfoAdapter";
|
||||
|
||||
private final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd");
|
||||
private final SimpleDateFormat TIME_FORMATTER = new SimpleDateFormat("HH:mm:ss");
|
||||
private final SimpleDateFormat DATETIME_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
||||
|
||||
static final int TX_RED = Color.rgb(255, 79, 65);
|
||||
static final int TX_GREEN = Color.rgb(54, 176, 91);
|
||||
@@ -60,8 +59,7 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
||||
this.listener = listener;
|
||||
Calendar cal = Calendar.getInstance();
|
||||
TimeZone tz = cal.getTimeZone(); //get the local time zone.
|
||||
DATE_FORMATTER.setTimeZone(tz);
|
||||
TIME_FORMATTER.setTimeZone(tz);
|
||||
DATETIME_FORMATTER.setTimeZone(tz);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -91,16 +89,15 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
||||
Collections.sort(data, new Comparator<TransactionInfo>() {
|
||||
@Override
|
||||
public int compare(TransactionInfo o1, TransactionInfo o2) {
|
||||
if ((o1.isPending) && (o2.isPending)) {
|
||||
long b1 = o1.timestamp;
|
||||
long b2 = o2.timestamp;
|
||||
return (b1 > b2) ? -1 : (b1 < b2) ? 1 : 0;
|
||||
long b1 = o1.timestamp;
|
||||
long b2 = o2.timestamp;
|
||||
if (b1 > b2) {
|
||||
return -1;
|
||||
} else if (b1 < b2) {
|
||||
return 1;
|
||||
} else {
|
||||
return o1.hash.compareTo(o2.hash);
|
||||
}
|
||||
if (o1.isPending) return -1;
|
||||
if (o2.isPending) return 1;
|
||||
long b1 = o1.blockheight;
|
||||
long b2 = o2.blockheight;
|
||||
return (b1 > b2) ? -1 : (b1 < b2) ? 1 : 0;
|
||||
}
|
||||
});
|
||||
this.infoItems.addAll(data);
|
||||
@@ -114,8 +111,8 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
||||
final TextView tvAmount;
|
||||
final TextView tvAmountPoint;
|
||||
final TextView tvAmountDecimal;
|
||||
final TextView tvDate;
|
||||
final TextView tvTime;
|
||||
final TextView tvPaymentId;
|
||||
final TextView tvDateTime;
|
||||
TransactionInfo infoItem;
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
@@ -124,16 +121,12 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
||||
// I know this is stupid but can't be bothered to align decimals otherwise
|
||||
this.tvAmountPoint = (TextView) itemView.findViewById(R.id.tx_amount_point);
|
||||
this.tvAmountDecimal = (TextView) itemView.findViewById(R.id.tx_amount_decimal);
|
||||
this.tvDate = (TextView) itemView.findViewById(R.id.tx_date);
|
||||
this.tvTime = (TextView) itemView.findViewById(R.id.tx_time);
|
||||
this.tvPaymentId = (TextView) itemView.findViewById(R.id.tx_paymentid);
|
||||
this.tvDateTime = (TextView) itemView.findViewById(R.id.tx_datetime);
|
||||
}
|
||||
|
||||
private String getDate(long time) {
|
||||
return DATE_FORMATTER.format(new Date(time * 1000));
|
||||
}
|
||||
|
||||
private String getTime(long time) {
|
||||
return TIME_FORMATTER.format(new Date(time * 1000));
|
||||
private String getDateTime(long time) {
|
||||
return DATETIME_FORMATTER.format(new Date(time * 1000));
|
||||
}
|
||||
|
||||
private void setTxColour(int clr) {
|
||||
@@ -147,25 +140,26 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
||||
String displayAmount = Wallet.getDisplayAmount(infoItem.amount);
|
||||
// TODO fix this with i8n code but cryptonote::print_money always uses '.' for decimal point
|
||||
String amountParts[] = displayAmount.split("\\.");
|
||||
amountParts[1] = amountParts[1].substring(0,5);
|
||||
|
||||
this.tvAmount.setText(amountParts[0]);
|
||||
this.tvAmountDecimal.setText(amountParts[1]);
|
||||
if (infoItem.isPending) {
|
||||
if (infoItem.isFailed) {
|
||||
this.tvAmount.setText('(' + amountParts[0]);
|
||||
this.tvAmountDecimal.setText(amountParts[1] + ')');
|
||||
setTxColour(TX_FAILED);
|
||||
} else if (infoItem.isPending) {
|
||||
setTxColour(TX_PENDING);
|
||||
if (infoItem.direction == TransactionInfo.Direction.Direction_Out) {
|
||||
this.tvAmount.setText('-' + amountParts[0]);
|
||||
}
|
||||
} else if (infoItem.isFailed) {
|
||||
this.tvAmount.setText('(' + amountParts[0]);
|
||||
this.tvAmountDecimal.setText(amountParts[1] + ')');
|
||||
setTxColour(TX_FAILED);
|
||||
} else if (infoItem.direction == TransactionInfo.Direction.Direction_In) {
|
||||
setTxColour(TX_GREEN);
|
||||
} else {
|
||||
setTxColour(TX_RED);
|
||||
}
|
||||
this.tvDate.setText(getDate(infoItem.timestamp));
|
||||
this.tvTime.setText(getTime(infoItem.timestamp));
|
||||
this.tvPaymentId.setText(infoItem.paymentId.equals("0000000000000000")?"":infoItem.paymentId);
|
||||
this.tvDateTime.setText(getDateTime(infoItem.timestamp));
|
||||
|
||||
itemView.setOnClickListener(this);
|
||||
}
|
||||
|
@@ -96,10 +96,13 @@ public class Wallet {
|
||||
|
||||
// virtual std::string keysFilename() const = 0;
|
||||
public boolean init(long upper_transaction_size_limit) {
|
||||
return initJ(WalletManager.getInstance().getDaemonAddress(), upper_transaction_size_limit);
|
||||
return initJ(WalletManager.getInstance().getDaemonAddress(), upper_transaction_size_limit,
|
||||
WalletManager.getInstance().getDaemonUsername(),
|
||||
WalletManager.getInstance().getDaemonPassword());
|
||||
}
|
||||
|
||||
private native boolean initJ(String daemon_address, long upper_transaction_size_limit);
|
||||
private native boolean initJ(String daemon_address, long upper_transaction_size_limit,
|
||||
String daemon_username, String daemon_password);
|
||||
|
||||
// virtual bool createWatchOnly(const std::string &path, const std::string &password, const std::string &language) const = 0;
|
||||
// virtual void setRefreshFromBlockHeight(uint64_t refresh_from_block_height) = 0;
|
||||
@@ -179,14 +182,15 @@ public class Wallet {
|
||||
long amount, int mixin_count,
|
||||
PendingTransaction.Priority priority) {
|
||||
disposePendingTransaction();
|
||||
long txHandle = createTransactionJ(dst_addr, payment_id, amount, mixin_count, priority);
|
||||
int _priority = priority.getValue();
|
||||
long txHandle = createTransactionJ(dst_addr, payment_id, amount, mixin_count, _priority);
|
||||
pendingTransaction = new PendingTransaction(txHandle);
|
||||
return pendingTransaction;
|
||||
}
|
||||
|
||||
private native long createTransactionJ(String dst_addr, String payment_id,
|
||||
long mount, int mixin_count,
|
||||
PendingTransaction.Priority priority);
|
||||
long amount, int mixin_count,
|
||||
int priority);
|
||||
|
||||
|
||||
public PendingTransaction createSweepUnmixableTransaction() {
|
||||
|
@@ -197,9 +197,12 @@ public class WalletManager {
|
||||
return testnet;
|
||||
}
|
||||
|
||||
public void setDaemon(String address, boolean testnet) {
|
||||
public void setDaemon(String address, boolean testnet, String username, String password) {
|
||||
//Log.d(TAG, "SETDAEMON " + username + "/" + password + "/" + address);
|
||||
this.daemonAddress = address;
|
||||
this.testnet = testnet;
|
||||
this.daemonUsername = username;
|
||||
this.daemonPassword = password;
|
||||
setDaemonAddressJ(address);
|
||||
}
|
||||
|
||||
@@ -213,6 +216,18 @@ public class WalletManager {
|
||||
|
||||
private native void setDaemonAddressJ(String address);
|
||||
|
||||
String daemonUsername = "";
|
||||
|
||||
public String getDaemonUsername() {
|
||||
return daemonUsername;
|
||||
}
|
||||
|
||||
String daemonPassword = "";
|
||||
|
||||
public String getDaemonPassword() {
|
||||
return daemonPassword;
|
||||
}
|
||||
|
||||
public native int getDaemonVersion();
|
||||
|
||||
public native long getBlockchainHeight();
|
||||
|
@@ -127,6 +127,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();
|
||||
@@ -224,6 +225,8 @@ public class WalletService extends Service {
|
||||
void onSentTransaction(boolean success);
|
||||
|
||||
void onSetNotes(boolean success);
|
||||
|
||||
void onWalletStarted(boolean success);
|
||||
}
|
||||
|
||||
String progressText = null;
|
||||
@@ -263,6 +266,8 @@ public class WalletService extends Service {
|
||||
private Looper mServiceLooper;
|
||||
private WalletService.ServiceHandler mServiceHandler;
|
||||
|
||||
private boolean errorState = false;
|
||||
|
||||
// Handler that receives messages from the thread
|
||||
private final class ServiceHandler extends Handler {
|
||||
ServiceHandler(Looper looper) {
|
||||
@@ -272,6 +277,11 @@ public class WalletService extends Service {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
Log.d(TAG, "Handling " + msg.arg2);
|
||||
if (errorState) {
|
||||
Log.i(TAG, "In error state.");
|
||||
// also, we have already stopped ourselves
|
||||
return;
|
||||
}
|
||||
switch (msg.arg2) {
|
||||
case START_SERVICE: {
|
||||
Bundle extras = msg.getData();
|
||||
@@ -283,7 +293,12 @@ public class WalletService extends Service {
|
||||
if (walletId != null) {
|
||||
showProgress(getString(R.string.status_wallet_loading));
|
||||
showProgress(10);
|
||||
start(walletId, walletPw); // TODO What if this fails?
|
||||
boolean success = start(walletId, walletPw);
|
||||
if (observer != null) observer.onWalletStarted(success);
|
||||
if (!success) {
|
||||
errorState = true;
|
||||
stop();
|
||||
}
|
||||
}
|
||||
} else if (cmd.equals(REQUEST_CMD_STORE)) {
|
||||
Wallet myWallet = getWallet();
|
||||
@@ -327,10 +342,6 @@ public class WalletService extends Service {
|
||||
} else if (cmd.equals(REQUEST_CMD_SEND)) {
|
||||
Wallet myWallet = getWallet();
|
||||
Log.d(TAG, "SEND TX for wallet: " + myWallet.getName());
|
||||
if (!myWallet.isTestNet()) {
|
||||
Log.e(TAG, "Sending transactions only on testnet");
|
||||
throw new IllegalStateException("Sending transactions only in testnet");
|
||||
}
|
||||
PendingTransaction pendingTransaction = myWallet.getPendingTransaction();
|
||||
if (pendingTransaction.getStatus() != PendingTransaction.Status.Status_Ok) {
|
||||
Log.e(TAG, "PendingTransaction is " + pendingTransaction.getStatus());
|
||||
@@ -461,7 +472,7 @@ public class WalletService extends Service {
|
||||
return true; // true is important so that onUnbind is also called next time
|
||||
}
|
||||
|
||||
private void start(String walletName, String walletPassword) {
|
||||
private boolean start(String walletName, String walletPassword) {
|
||||
startNotfication();
|
||||
// if there is an listener it is always started / syncing
|
||||
Log.d(TAG, "start()");
|
||||
@@ -470,7 +481,10 @@ public class WalletService extends Service {
|
||||
if (listener == null) {
|
||||
Log.d(TAG, "start() loadWallet");
|
||||
Wallet aWallet = loadWallet(walletName, walletPassword);
|
||||
// TODO check aWallet and die gracefully if neccessary
|
||||
if ((aWallet == null) || (aWallet.getConnectionStatus() != Wallet.ConnectionStatus.ConnectionStatus_Connected)) {
|
||||
if (aWallet != null) aWallet.close();
|
||||
return false;
|
||||
}
|
||||
listener = new MyWalletListener(aWallet);
|
||||
listener.start();
|
||||
showProgress(100);
|
||||
@@ -480,6 +494,7 @@ public class WalletService extends Service {
|
||||
// if we try to refresh the history here we get occasional segfaults!
|
||||
// doesnt matter since we update as soon as we get a new block anyway
|
||||
Log.d(TAG, "start() done");
|
||||
return true;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
@@ -512,7 +527,7 @@ public class WalletService extends Service {
|
||||
showProgress(55);
|
||||
wallet.init(0);
|
||||
showProgress(90);
|
||||
Log.d(TAG, wallet.getConnectionStatus().toString());
|
||||
//Log.d(TAG, wallet.getConnectionStatus().toString());
|
||||
}
|
||||
return wallet;
|
||||
}
|
||||
|
28
app/src/main/java/com/m2049r/xmrwallet/util/BarcodeData.java
Normal file
28
app/src/main/java/com/m2049r/xmrwallet/util/BarcodeData.java
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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;
|
||||
//String amount = null:
|
||||
|
||||
public BarcodeData(String address, String paymentId) {
|
||||
this.address = address;
|
||||
this.paymentId = paymentId;
|
||||
}
|
||||
}
|
@@ -27,6 +27,7 @@ import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import com.m2049r.xmrwallet.R;
|
||||
import com.m2049r.xmrwallet.model.WalletManager;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@@ -71,6 +72,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 +126,14 @@ 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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -75,4 +75,19 @@ public class TxData implements Parcelable {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append("dst_addr:");
|
||||
sb.append(dst_addr);
|
||||
sb.append(",paymentId:");
|
||||
sb.append(paymentId);
|
||||
sb.append(",amount:");
|
||||
sb.append(amount);
|
||||
sb.append(",mixin:");
|
||||
sb.append(mixin);
|
||||
sb.append(",priority:");
|
||||
sb.append(priority.toString());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
@@ -5,5 +5,5 @@
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z"/>
|
||||
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||
</vector>
|
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M11.5,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zm6.5,-6v-5.5c0,-3.07 -2.13,-5.64 -5,-6.32V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5v0.68c-2.87,0.68 -5,3.25 -5,6.32V16l-2,2v1h17v-1l-2,-2z" />
|
||||
</vector>
|
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01,-.25 1.97,-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0,-4.42,-3.58,-8,-8,-8zm0 14c-3.31 0,-6,-2.69,-6,-6 0,-1.01.25,-1.97.7,-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4,-4,-4,-4v3z" />
|
||||
</vector>
|
@@ -4,6 +4,20 @@
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8sp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/pbProgress"
|
||||
style="@android:style/Widget.ProgressBar.Horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="invisible" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -12,7 +26,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvWalletLabel"
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="0sp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/generate_wallet_label"
|
||||
@@ -22,7 +36,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvWalletPasswordLabel"
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="0sp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/generate_password_label"
|
||||
@@ -58,20 +72,6 @@
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/pbProgress"
|
||||
style="@android:style/Widget.ProgressBar.Horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="invisible" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvWalletMnemonicLabel"
|
||||
android:layout_width="match_parent"
|
||||
@@ -103,10 +103,10 @@
|
||||
android:id="@+id/tvWalletAddress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:selectAllOnFocus="true"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/colorPrimaryDark"
|
||||
android:textIsSelectable="true"
|
||||
android:selectAllOnFocus="true"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<TextView
|
||||
@@ -123,10 +123,10 @@
|
||||
android:id="@+id/tvWalletViewKey"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:selectAllOnFocus="true"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/colorPrimaryDark"
|
||||
android:textIsSelectable="true"
|
||||
android:selectAllOnFocus="true"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<TextView
|
||||
@@ -143,10 +143,10 @@
|
||||
android:id="@+id/tvWalletSpendKey"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:selectAllOnFocus="true"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/colorPrimaryDark"
|
||||
android:textIsSelectable="true"
|
||||
android:selectAllOnFocus="true"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<Button
|
||||
@@ -157,43 +157,4 @@
|
||||
android:background="@color/colorPrimary"
|
||||
android:text="@string/generate_button_accept"
|
||||
android:visibility="gone" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llFunctions"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="2dip"
|
||||
android:background="@color/colorPrimary" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/bBackup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@color/colorPrimaryDark"
|
||||
android:text="@string/generate_button_backup" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/bExport"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@color/colorPrimaryDark"
|
||||
android:text="@string/generate_button_export" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/bDelete"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:background="@color/colorAccent"
|
||||
android:text="@string/generate_button_delete" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
@@ -6,7 +6,7 @@
|
||||
android:orientation="vertical">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/tbLogin"
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/fragment_container"
|
||||
android:layout_margin="4sp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
|
@@ -17,35 +17,49 @@
|
||||
android:textOff="@string/connect_testnet"
|
||||
android:textOn="@string/connect_mainnet"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
</ToggleButton>
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etDaemonAddress"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:gravity="center"
|
||||
android:hint="@string/prompt_daemon"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/colorPrimary"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
android:clickable="true"
|
||||
android:inputType="text"
|
||||
android:gravity="center"
|
||||
android:hint="@string/prompt_daemon"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="textWebEmailAddress|textNoSuggestions"
|
||||
android:maxLines="1"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="15sp"
|
||||
android:maxLines="1"
|
||||
android:imeOptions="actionDone"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/tbMainNet"
|
||||
app:layout_constraintRight_toLeftOf="@+id/tbMainNet"
|
||||
app:layout_constraintLeft_toLeftOf="parent" />
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toLeftOf="@+id/tbMainNet" />
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
||||
<ListView
|
||||
android:id="@+id/list"
|
||||
android:layout_height="wrap_content"
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginTop="4dp">
|
||||
</ListView>
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ListView
|
||||
android:id="@+id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"/>
|
||||
|
||||
<android.support.design.widget.FloatingActionButton xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/fabAdd"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="16sp"
|
||||
android:src="@drawable/ic_add_black_24dp"
|
||||
app:elevation="6dp"
|
||||
app:fabSize="auto"
|
||||
app:pressedTranslationZ="12dp" />
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,96 +1,80 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<android.support.v7.widget.CardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/card"
|
||||
android:layout_width="match_parent"
|
||||
app:cardBackgroundColor="@color/main_background"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="1dp">
|
||||
android:layout_margin="1sp"
|
||||
app:cardBackgroundColor="@color/main_background">
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
<android.support.constraint.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8sp"
|
||||
android:layout_marginTop="8sp"
|
||||
tools:layout_editor_absoluteX="0sp"
|
||||
tools:layout_editor_absoluteY="0sp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tx_amount"
|
||||
android:layout_width="60sp"
|
||||
android:layout_height="wrap_content"
|
||||
tools:layout_editor_absoluteY="0dp"
|
||||
tools:layout_editor_absoluteX="0dp">
|
||||
android:gravity="end"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:text="9999999"
|
||||
android:textColor="@android:color/holo_red_light"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tx_amount"
|
||||
android:gravity="end"
|
||||
android:layout_width="70sp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="99999999"
|
||||
android:textColor="@android:color/holo_red_light"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
android:layout_marginLeft="8dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
<TextView
|
||||
android:id="@+id/tx_amount_point"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:text="."
|
||||
android:textColor="@android:color/holo_red_light"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/tx_amount"
|
||||
app:layout_constraintLeft_toRightOf="@+id/tx_amount"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tx_amount_point"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="."
|
||||
android:textColor="@android:color/holo_red_light"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintLeft_toRightOf="@+id/tx_amount"
|
||||
android:layout_marginLeft="0dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
<TextView
|
||||
android:id="@+id/tx_amount_decimal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:text="99999"
|
||||
android:textColor="@android:color/holo_red_light"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/tx_amount"
|
||||
app:layout_constraintLeft_toRightOf="@+id/tx_amount_point"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tx_amount_decimal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="999999999999"
|
||||
android:textColor="@android:color/holo_red_light"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintLeft_toRightOf="@+id/tx_amount_point"
|
||||
android:layout_marginLeft="0dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
<TextView
|
||||
android:id="@+id/tx_paymentid"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8sp"
|
||||
android:textSize="13sp"
|
||||
android:text="0123456789abcdef"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/tx_amount"
|
||||
app:layout_constraintRight_toLeftOf="@+id/tx_datetime"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tx_date"
|
||||
android:textColor="@android:color/black"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:text="2017-05-22"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:layout_constraintRight_toLeftOf="@+id/tx_time"
|
||||
android:layout_marginRight="8dp"/>
|
||||
<TextView
|
||||
android:id="@+id/tx_datetime"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:text="2017-05-22 21:32"
|
||||
android:textColor="@android:color/black"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/tx_amount"
|
||||
app:layout_constraintRight_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tx_time"
|
||||
android:textColor="@android:color/black"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:text="21:32:11"
|
||||
app:layout_constraintRight_toLeftOf="parent"
|
||||
android:layout_marginRight="8dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
||||
</android.support.v7.widget.CardView>
|
||||
</android.support.v7.widget.CardView>
|
||||
|
@@ -46,6 +46,22 @@
|
||||
android:textIsSelectable="true" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
|
||||
<TextView
|
||||
android:gravity="right"
|
||||
android:padding="8dp"
|
||||
android:text="@string/tx_destination"
|
||||
android:textColor="@color/colorAccent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvDestination"
|
||||
android:gravity="left"
|
||||
android:padding="8dip"
|
||||
android:selectAllOnFocus="true"
|
||||
android:textIsSelectable="true" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
|
||||
<TextView
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user