1
mirror of https://github.com/m2049r/xmrwallet synced 2025-09-05 09:58:42 +02:00

Compare commits

..

64 Commits
v0.4.1 ... v0.6

Author SHA1 Message Date
m2049r
093e4bda1d Doc & Version update (#43)
* added hint that amount is in XMR

* update FAQ and version
2017-09-04 23:29:48 +02:00
m2049r
4aa7000cb3 added hint that amount is in XMR (#42)
added hint that amount is in XMR when receiving
2017-09-04 23:27:04 +02:00
m2049r
62844192df Merge pull request #41 from m2049r/bugfix_logo
fixed logo svg
2017-09-04 23:21:29 +02:00
m2049r
855b35a6b7 added l in svg path 2017-09-04 22:52:10 +02:00
m2049r
88110b702f Merge pull request #40 from m2049r/bugfix_issue_37
request and respond to permissions marshmallow style
2017-09-04 22:32:30 +02:00
m2049r
0013caa05f request and respond to permissions marshmallow style 2017-09-04 22:30:37 +02:00
m2049r
5b3e92e91a Merge pull request #39 from m2049r/bugfix_issue_36
Use AsyncTask with 5MB stack in lots of places
2017-09-04 20:51:21 +02:00
m2049r
f8ea3cc77f use AsyncTask with 5MB stack in lots of places 2017-09-04 20:43:05 +02:00
m2049r
59ef2a1f8e Merge pull request #33 from KeeJef/master
Clarifying some Strings
2017-09-04 18:26:30 +02:00
m2049r
fae07ed716 Merge pull request #38 from m2049r/feature_rename_wallet
Rename/Backup/Archive Wallet FIles
2017-09-04 07:34:48 +02:00
m2049r
e662d5a9c0 wallet rename + tweaks 2017-09-04 07:26:04 +02:00
m2049r
4b2d52fbe6 wallet archive/backup progress dialogs 2017-09-03 21:29:15 +02:00
m2049r
ec7798cb34 wallet archive/backup 2017-09-03 14:09:27 +02:00
m2049r
20e7d6d065 rename option in wallet list + tweaks 2017-09-03 11:55:42 +02:00
m2049r
89ada5b294 Merge pull request #35 from m2049r/feature_info_alert
Alert before showing wallet details
2017-09-03 09:44:37 +02:00
m2049r
fe80fef36e alert before showing wallet details 2017-09-03 09:43:52 +02:00
m2049r
e8331dda7a Merge pull request #34 from m2049r/feature_qr_receive
QR Code for receiving
2017-09-02 12:56:17 +02:00
m2049r
68abaec6cb tweaks + get address from wallet (and not from file) 2017-09-02 12:51:57 +02:00
m2049r
fe84bae9ed rework options menus 2017-09-02 10:05:50 +02:00
KeeJef
1289a0ada4 Clarifying some Strings
Just for new people i think the amount when sending should indicate that the values are XMR not USD or something
2017-09-02 13:08:09 +10:00
m2049r
93025c5e1b Receive QR code 2017-09-02 00:00:54 +02:00
m2049r
e0439a1359 Merge pull request #32 from m2049r/bugfix_scanbutton
fix scan button enable/disable
2017-08-31 18:48:27 +02:00
m2049r
93ec3865da fix scan button enable/disable 2017-08-31 18:43:51 +02:00
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
m2049r
2d42a0287d Merge pull request #25 from m2049r/docs
updated version number + docs
2017-08-29 23:41:38 +02:00
m2049r
2ebb9d0650 updated version number + docs 2017-08-29 23:40:46 +02:00
m2049r
30792c53c0 Merge pull request #24 from m2049r/feature_spend_mainnet
mainnet spending
2017-08-29 22:37:23 +02:00
m2049r
3846a0c391 mainnet spending 2017-08-29 22:22:07 +02:00
m2049r
c66ebd654a Merge pull request #23 from m2049r/cleanup
Lots of GUI tweaks
2017-08-29 22:07:17 +02:00
m2049r
186ed5cd39 second spend confirmation for mainnet 2017-08-29 22:05:51 +02:00
m2049r
e39cd1c988 lots of GUI tweaks 2017-08-29 22:05:45 +02:00
m2049r
39d9c4d0c3 Merge pull request #22 from m2049r/feature_colours
balance colour + tx ordering
2017-08-29 08:44:36 +02:00
m2049r
e4562f07a4 balance colour + tx ordering 2017-08-29 08:43:56 +02:00
m2049r
b3ea4540a1 Merge pull request #21 from m2049r/feature_toolbar
Toolbars + action & context menu
2017-08-28 23:51:36 +02:00
m2049r
ea7c7d2fdb net colour in toolbar 2017-08-28 23:46:18 +02:00
m2049r
8ff8be6f60 list context menu + tweaks 2017-08-28 23:14:53 +02:00
m2049r
b5ded700fe toolbar info action for wallet activity 2017-08-28 22:10:17 +02:00
m2049r
a5527d4efd use default daemon ports if none supplied 2017-08-28 19:38:13 +02:00
m2049r
a007810824 Merge pull request #20 from m2049r/feature_list_addfab
nicer add icon
2017-08-27 22:58:36 +02:00
m2049r
a634cf18b1 nicer fab icon 2017-08-27 22:57:35 +02:00
m2049r
0b2fa08df8 Merge pull request #19 from m2049r/feature_list_addfab
Create wallet by action button
2017-08-27 09:14:52 +02:00
m2049r
191aa6ac22 action button for create wallet + setting activity title in LoginActivity 2017-08-27 09:12:53 +02:00
m2049r
a82a90575d deamon prompt show syntax 2017-08-26 00:34:17 +02:00
m2049r
2e04046f50 Merge pull request #18 from m2049r/feature_layout
simple layout
2017-08-26 00:29:41 +02:00
m2049r
c11d577c5f simple layout
minimum number of decimal places
progress at top
2017-08-26 00:27:27 +02:00
m2049r
74b2dd209c Merge pull request #17 from m2049r/feature_daemon_userpassword
Username/Password for daemon
2017-08-24 00:06:18 +02:00
m2049r
cb1d541474 Username/Password for daemon 2017-08-24 00:03:23 +02:00
m2049r
807d217aac Merge pull request #16 from m2049r/feature_txrecipient
show destinations in tx view
2017-08-22 13:10:14 +02:00
m2049r
0421bcfac3 show destination in tx view 2017-08-22 13:02:31 +02:00
m2049r
b6c4e06fda Merge pull request #15 from m2049r/feature_spend_redo
enable dispose of prepared transaction
2017-08-22 11:10:19 +02:00
m2049r
3ee8343074 enable dispose of prepared transaction 2017-08-22 11:05:43 +02:00
m2049r
110621824a Merge pull request #14 from m2049r/hotfix_priority
passing of transaction priority fixed
2017-08-22 10:56:07 +02:00
m2049r
7194580fbe passing of transaction priority fixed 2017-08-22 10:54:16 +02:00
55 changed files with 2751 additions and 891 deletions

2
.idea/.gitignore generated vendored
View File

@@ -1,2 +1,2 @@
workspace.xml
markdown-navigator*
markdown-*

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

@@ -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
@@ -22,16 +21,12 @@ You may loose all your Moneroj if you use this App.
- Monerujo means "Monero Wallet" according to https://www.reddit.com/r/Monero/comments/3exy7t/esperanto_corner/
### 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 +40,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

View File

@@ -7,8 +7,8 @@ android {
applicationId "com.m2049r.xmrwallet"
minSdkVersion 21
targetSdkVersion 25
versionCode 5
versionName "0.4.1"
versionCode 11
versionName "0.6"
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,10 +7,11 @@
<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"
android:icon="@mipmap/ic_launcher"
android:icon="@drawable/ic_monero_32dp"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">

View File

@@ -461,7 +461,6 @@ Java_com_m2049r_xmrwallet_model_WalletManager_resolveOpenAlias(JNIEnv *env, jobj
//TODO static std::tuple<bool, std::string, std::string, std::string, std::string> checkUpdates(const std::string &software, const std::string &subdir);
// actually a WalletManager function, but logically in onWalletSelected
JNIEXPORT jboolean JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_closeJ(JNIEnv *env, jobject instance,
jobject walletInstance) {
@@ -592,12 +591,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;
}

View File

@@ -16,12 +16,13 @@
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;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
@@ -68,12 +69,9 @@ public class GenerateFragment extends Fragment {
bGenerate = (Button) view.findViewById(R.id.bGenerate);
etWalletMnemonic.setRawInputType(InputType.TYPE_CLASS_TEXT);
etWalletAddress.setRawInputType(InputType.TYPE_CLASS_TEXT);
etWalletViewKey.setRawInputType(InputType.TYPE_CLASS_TEXT);
etWalletSpendKey.setRawInputType(InputType.TYPE_CLASS_TEXT);
boolean testnet = WalletManager.getInstance().isTestNet();
//etWalletMnemonic.setTextIsSelectable(testnet);
etWalletAddress.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
etWalletViewKey.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
etWalletSpendKey.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
Helper.showKeyboard(getActivity());
etWalletName.addTextChangedListener(new TextWatcher() {
@@ -297,8 +295,12 @@ public class GenerateFragment extends Fragment {
private void generateWallet() {
String name = etWalletName.getText().toString();
if (name.length() == 0) return;
String walletPath = Helper.getWalletPath(getActivity(), name);
if (WalletManager.getInstance().walletExists(walletPath)) {
if (name.charAt(0)=='.') {
Toast.makeText(getActivity(), getString(R.string.generate_wallet_dot), Toast.LENGTH_LONG).show();
etWalletName.requestFocus();
}
File walletFile = Helper.getWalletFile(getActivity(), name);
if (WalletManager.getInstance().walletExists(walletFile)) {
Toast.makeText(getActivity(), getString(R.string.generate_wallet_exists), Toast.LENGTH_LONG).show();
etWalletName.requestFocus();
return;
@@ -344,6 +346,13 @@ public class GenerateFragment extends Fragment {
bGenerate.setVisibility(View.VISIBLE);
}
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume()");
activityCallback.setTitle(getString(R.string.generate_title));
}
GenerateFragment.Listener activityCallback;
public interface Listener {
@@ -354,6 +363,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

View File

@@ -16,9 +16,11 @@
package com.m2049r.xmrwallet;
import android.app.Fragment;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -28,15 +30,13 @@ import android.widget.TextView;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.service.MoneroHandlerThread;
import java.io.File;
import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor;
public class GenerateReviewFragment extends Fragment {
static final String TAG = "GenerateReviewFragment";
static final public String VIEW_DETAILS = "details";
static final public String VIEW_ACCEPT = "accept";
static final public String VIEW_WALLET = "wallet";
static final public String VIEW_TYPE_DETAILS = "details";
static final public String VIEW_TYPE_ACCEPT = "accept";
static final public String VIEW_TYPE_WALLET = "wallet";
ProgressBar pbProgress;
TextView tvWalletName;
@@ -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
@@ -75,16 +76,12 @@ public class GenerateReviewFragment extends Fragment {
showProgress();
Bundle b = getArguments();
String type = b.getString("type");
if (!type.equals(VIEW_WALLET)) {
String name = b.getString("name");
String password = b.getString("password");
tvWalletName.setText(new File(name).getName());
show(name, password, type);
} else {
show(walletCallback.getWallet(), null, type);
}
Bundle args = getArguments();
String path = args.getString("path");
String password = args.getString("password");
String type = args.getString("type");
new AsyncShow().executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR,
path, password, type);
return view;
}
@@ -95,40 +92,64 @@ public class GenerateReviewFragment extends Fragment {
acceptCallback.onAccept(name, password);
}
private void show(final String walletPath, final String password, final String type) {
new Thread(null,
new Runnable() {
@Override
public void run() {
final Wallet wallet = WalletManager.getInstance().openWallet(walletPath, password);
getActivity().runOnUiThread(new Runnable() {
public void run() {
show(wallet, password, type);
wallet.close();
}
});
}
}
, "DetailsReview", MoneroHandlerThread.THREAD_STACK_SIZE).start();
}
private class AsyncShow extends AsyncTask<String, Void, Boolean> {
String type;
String password;
private void show(final Wallet wallet, final String password, final String type) {
if (type.equals(GenerateReviewFragment.VIEW_ACCEPT)) {
tvWalletPassword.setText(password);
bAccept.setVisibility(View.VISIBLE);
bAccept.setEnabled(true);
String name;
String address;
String seed;
String viewKey;
boolean isWatchOnly;
@Override
protected Boolean doInBackground(String... params) {
if (params.length != 3) return false;
String walletPath = params[0];
password = params[1];
type = params[2];
Wallet wallet;
boolean closeWallet;
if (type.equals(GenerateReviewFragment.VIEW_TYPE_WALLET)) {
wallet = GenerateReviewFragment.this.walletCallback.getWallet();
closeWallet = false;
} else {
wallet = WalletManager.getInstance().openWallet(walletPath, password);
closeWallet = true;
}
if (wallet.getStatus() != Wallet.Status.Status_Ok) return false;
name = wallet.getName();
address = wallet.getAddress();
seed = wallet.getSeed();
viewKey = wallet.getSecretViewKey();
isWatchOnly = wallet.isWatchOnly();
if (closeWallet) wallet.close();
return true;
}
tvWalletName.setText(wallet.getName());
tvWalletAddress.setText(wallet.getAddress());
tvWalletMnemonic.setText(wallet.getSeed());
tvWalletViewKey.setText(wallet.getSecretViewKey());
String spend = wallet.isWatchOnly() ? "" : "not available - use seed for recovery";
if (spend.length() > 0) { //TODO should be == 64, but spendkey is not in the API yet
tvWalletSpendKey.setText(spend);
} else {
tvWalletSpendKey.setText(getString(R.string.generate_wallet_watchonly));
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (result) {
if (type.equals(GenerateReviewFragment.VIEW_TYPE_ACCEPT)) {
tvWalletPassword.setText(password);
bAccept.setVisibility(View.VISIBLE);
bAccept.setEnabled(true);
}
tvWalletName.setText(name);
tvWalletAddress.setText(address);
tvWalletMnemonic.setText(seed);
tvWalletViewKey.setText(viewKey);
String spend = isWatchOnly ? "" : "not available - use seed for recovery";
if (spend.length() > 0) { //TODO should be == 64, but spendkey is not in the API yet
tvWalletSpendKey.setText(spend);
} else {
tvWalletSpendKey.setText(getString(R.string.generate_wallet_watchonly));
}
}
hideProgress();
}
hideProgress();
}
GenerateReviewFragment.Listener acceptCallback = null;
@@ -140,6 +161,7 @@ public class GenerateReviewFragment extends Fragment {
public interface ListenerWithWallet {
Wallet getWallet();
}
@Override

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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,12 +16,11 @@
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.v4.app.Fragment;
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 bScan;
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);
bScan = (Button) view.findViewById(R.id.bScan);
bSweep = (Button) view.findViewById(R.id.bSweep);
bPrepareSend = (Button) view.findViewById(R.id.bPrepareSend);
bPaymentId = (Button) view.findViewById(R.id.bPaymentId);
bDispose = (Button) view.findViewById(R.id.bDispose);
llConfirmSend = (LinearLayout) view.findViewById(R.id.llConfirmSend);
tvTxAmount = (TextView) view.findViewById(R.id.tvTxAmount);
@@ -93,17 +96,14 @@ 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);
etAddress.setRawInputType(InputType.TYPE_CLASS_TEXT);
etPaymentId.setRawInputType(InputType.TYPE_CLASS_TEXT);
etAddress.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
etPaymentId.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
etNotes.setRawInputType(InputType.TYPE_CLASS_TEXT);
// 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,10 +191,26 @@ public class SendFragment extends Fragment {
}
});
bDispose.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
activityCallback.onDisposeRequest();
enableEdit();
}
});
bScan.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
activityCallback.onScanAddress();
}
});
bPaymentId.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
etPaymentId.setText((activityCallback.generatePaymentId()));
etPaymentId.setText((Wallet.generatePaymentId()));
etPaymentId.setSelection(etPaymentId.getText().length());
}
});
@@ -224,6 +240,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 +276,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() {
@@ -254,7 +286,7 @@ public class SendFragment extends Fragment {
private boolean paymentIdOk() {
String paymentId = etPaymentId.getText().toString();
return paymentId.isEmpty() || activityCallback.isPaymentIdValid(paymentId);
return paymentId.isEmpty() || Wallet.isPaymentIdValid(paymentId);
}
private void prepareSend() {
@@ -290,6 +322,7 @@ public class SendFragment extends Fragment {
etAddress.setEnabled(false);
etPaymentId.setEnabled(false);
etAmount.setEnabled(false);
bScan.setEnabled(false);
bPaymentId.setEnabled(false);
bSweep.setEnabled(false);
bPrepareSend.setEnabled(false);
@@ -301,9 +334,14 @@ public class SendFragment extends Fragment {
etAddress.setEnabled(true);
etPaymentId.setEnabled(true);
etAmount.setEnabled(true);
bScan.setEnabled(true);
bPaymentId.setEnabled(true);
bSweep.setEnabled(true);
bPrepareSend.setEnabled(true);
llConfirmSend.setVisibility(View.GONE);
bSend.setEnabled(true);
etNotes.setEnabled(true);
bReallySend.setVisibility(View.GONE);
}
private void send() {
@@ -321,12 +359,53 @@ public class SendFragment extends Fragment {
void onSend(String notes);
String generatePaymentId();
boolean isPaymentIdValid(String paymentId);
String getWalletAddress();
void onDisposeRequest();
void onScanAddress();
BarcodeData getScannedData();
}
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume");
BarcodeData data = activityCallback.getScannedData();
if (data != null) {
String scannedAddress = data.address;
if (scannedAddress != null) {
etAddress.setText(scannedAddress);
} else {
etAddress.getText().clear();
}
String scannedPaymenId = data.paymentId;
if (scannedPaymenId != null) {
etPaymentId.setText(scannedPaymenId);
} else {
etPaymentId.getText().clear();
}
if (data.amount >= 0) {
String scannedAmount = Helper.getDisplayAmount(data.amount);
etAmount.setText(scannedAmount);
} else {
etAmount.getText().clear();
}
etAmount.requestFocus();
etAmount.setSelection(etAmount.getText().length());
} else { // no scan data
// jump to first empty field
if (etAddress.getText().toString().isEmpty()) {
etAddress.requestFocus();
} else if (etPaymentId.getText().toString().isEmpty()) {
etPaymentId.requestFocus();
} else {
etAmount.requestFocus();
etAmount.setSelection(etAmount.getText().length());
}
}
}
@Override
@@ -350,7 +429,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) {

View File

@@ -16,31 +16,28 @@
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;
import java.util.HashSet;
import java.util.Set;
import java.util.TimeZone;
public class TxFragment extends Fragment {
@@ -48,7 +45,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 +57,7 @@ public class TxFragment extends Fragment {
TextView tvTxTimestamp;
TextView tvTxId;
TextView tvTxKey;
TextView tvDestination;
TextView tvTxPaymentId;
TextView tvTxBlockheight;
TextView tvTxAmount;
@@ -78,6 +76,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 +141,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 +153,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 +191,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 +216,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);
}

File diff suppressed because it is too large Load Diff

View File

@@ -16,17 +16,18 @@
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.v7.app.AlertDialog;
import android.support.annotation.Nullable;
import android.support.constraint.ConstraintLayout;
import android.support.v4.app.Fragment;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
@@ -36,9 +37,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;
@@ -49,14 +49,27 @@ 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;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
if (activityCallback.hasWallet())
inflater.inflate(R.menu.wallet_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@@ -64,11 +77,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(Helper.getDisplayAmount(0));
tvUnconfirmedAmount = (TextView) view.findViewById(R.id.tvUnconfirmedAmount);
tvUnconfirmedAmount.setText(Helper.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 +133,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 +160,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 +173,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;
}
@@ -175,8 +192,16 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
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(Helper.getDisplayAmount(unlockedBalance));
tvUnconfirmedAmount.setText(Helper.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 +222,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 +242,8 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
void setTitle(String title);
void setSubtitle(String subtitle);
void onSendRequest();
void onTxDetailsRequest(TransactionInfo info);
@@ -225,6 +253,10 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
boolean isWatchOnly();
String getTxKey(String txId);
void onWalletReceive();
boolean hasWallet();
}
@Override

View File

@@ -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);
}

View File

@@ -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() {
@@ -234,6 +238,7 @@ public class Wallet {
public native void setDefaultMixin(int mixin);
public native boolean setUserNote(String txid, String note);
public native String getUserNote(String txid);
public native String getTxKey(String txid);

View File

@@ -71,8 +71,8 @@ public class WalletManager {
managedWallets.remove(walletId);
}
public Wallet createWallet(String path, String password, String language) {
long walletHandle = createWalletJ(path, password, language, isTestNet());
public Wallet createWallet(File aFile, String password, String language) {
long walletHandle = createWalletJ(aFile.getAbsolutePath(), password, language, isTestNet());
Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet.getName(), wallet);
return wallet;
@@ -89,14 +89,14 @@ public class WalletManager {
private native long openWalletJ(String path, String password, boolean isTestNet);
public Wallet recoveryWallet(String path, String mnemonic) {
Wallet wallet = recoveryWallet(path, mnemonic, 0);
public Wallet recoveryWallet(File aFile, String mnemonic) {
Wallet wallet = recoveryWallet(aFile, mnemonic, 0);
manageWallet(wallet.getName(), wallet);
return wallet;
}
public Wallet recoveryWallet(String path, String mnemonic, long restoreHeight) {
long walletHandle = recoveryWalletJ(path, mnemonic, isTestNet(), restoreHeight);
public Wallet recoveryWallet(File aFile, String mnemonic, long restoreHeight) {
long walletHandle = recoveryWalletJ(aFile.getAbsolutePath(), mnemonic, isTestNet(), restoreHeight);
Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet.getName(), wallet);
return wallet;
@@ -104,9 +104,9 @@ public class WalletManager {
private native long recoveryWalletJ(String path, String mnemonic, boolean isTestNet, long restoreHeight);
public Wallet createWalletFromKeys(String path, String language, long restoreHeight,
public Wallet createWalletFromKeys(File aFile, String language, long restoreHeight,
String addressString, String viewKeyString, String spendKeyString) {
long walletHandle = createWalletFromKeysJ(path, language, isTestNet(), restoreHeight,
long walletHandle = createWalletFromKeysJ(aFile.getAbsolutePath(), language, isTestNet(), restoreHeight,
addressString, viewKeyString, spendKeyString);
Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet.getName(), wallet);
@@ -134,6 +134,10 @@ public class WalletManager {
return closed;
}
public boolean walletExists(File aFile) {
return walletExists(aFile.getAbsolutePath());
}
public native boolean walletExists(String path);
public native boolean verifyWalletPassword(String keys_file_name, String password, boolean watch_only);
@@ -146,6 +150,31 @@ public class WalletManager {
public String address;
}
public WalletInfo getWalletInfo(File wallet) {
WalletInfo info = new WalletInfo();
info.path = wallet.getParentFile();
info.name = wallet.getName();
File addressFile = new File(info.path, info.name + ".address.txt");
//Log.d(TAG, addressFile.getAbsolutePath());
info.address = "??????";
BufferedReader addressReader = null;
try {
addressReader = new BufferedReader(new FileReader(addressFile));
info.address = addressReader.readLine();
} catch (IOException ex) {
Log.d(TAG, ex.getLocalizedMessage());
} finally {
if (addressReader != null) {
try {
addressReader.close();
} catch (IOException ex) {
// that's just too bad
}
}
}
return info;
}
public List<WalletInfo> findWallets(File path) {
List<WalletInfo> wallets = new ArrayList<>();
Log.d(TAG, "Scanning: " + path.getAbsolutePath());
@@ -155,29 +184,9 @@ public class WalletManager {
}
});
for (int i = 0; i < found.length; i++) {
WalletInfo info = new WalletInfo();
info.path = path;
String filename = found[i].getName();
info.name = filename.substring(0, filename.length() - 5); // 5 is length of ".keys"+1
File addressFile = new File(path, info.name + ".address.txt");
//Log.d(TAG, addressFile.getAbsolutePath());
info.address = "??????";
BufferedReader addressReader = null;
try {
addressReader = new BufferedReader(new FileReader(addressFile));
info.address = addressReader.readLine();
} catch (IOException ex) {
Log.d(TAG, ex.getLocalizedMessage());
} finally {
if (addressReader != null) {
try {
addressReader.close();
} catch (IOException ex) {
// that's just too bad
}
}
}
wallets.add(info);
File f = new File(found[i].getParent(), filename.substring(0, filename.length() - 5)); // 5 is length of ".keys"+1
wallets.add(getWalletInfo(f));
}
return wallets;
}
@@ -197,9 +206,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 +225,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();

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();
@@ -224,6 +222,8 @@ public class WalletService extends Service {
void onSentTransaction(boolean success);
void onSetNotes(boolean success);
void onWalletStarted(boolean success);
}
String progressText = null;
@@ -263,6 +263,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 +274,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 +290,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 +339,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());
@@ -389,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",
@@ -408,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
@@ -427,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
@@ -461,7 +465,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 +474,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 +487,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() {
@@ -499,6 +507,7 @@ public class WalletService extends Service {
}
stopForeground(true);
stopSelf();
Running = false;
}
private Wallet loadWallet(String walletName, String walletPassword) {
@@ -512,13 +521,13 @@ 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;
}
private Wallet openWallet(String walletName, String walletPassword) {
String path = Helper.getWalletPath(getApplicationContext(), walletName);
String path = Helper.getWalletFile(getApplicationContext(), walletName).getAbsolutePath();
showProgress(20);
Wallet wallet = null;
WalletManager walletMgr = WalletManager.getInstance();
@@ -548,7 +557,7 @@ public class WalletService extends Service {
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new Notification.Builder(this)
.setContentTitle(getString(R.string.service_description))
.setSmallIcon(R.drawable.ic_notification_sync_32_32)
.setSmallIcon(R.drawable.ic_monero_32dp)
.setContentIntent(pendingIntent)
.build();
startForeground(NOTIFICATION_ID, notification);

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

@@ -21,12 +21,21 @@ import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.VectorDrawable;
import android.os.Environment;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import java.io.File;
@@ -71,12 +80,34 @@ public class Helper {
}
}
static public String getWalletPath(Context context, String aWalletName) {
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) {
// return getWalletFile(context, aWalletName).getAbsolutePath();
// }
static public File getWalletFile(Context context, String aWalletName) {
File walletDir = getStorageRoot(context);
//d(TAG, "walletdir=" + walletDir.getAbsolutePath());
File f = new File(walletDir, aWalletName);
Log.d(TAG, "wallet = " + f.getAbsolutePath() + " size=" + f.length());
return f.getAbsolutePath();
return f;
}
/* Checks if external storage is available for read and write */
@@ -107,4 +138,51 @@ 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);
}
public static Bitmap getBitmap(Context context, int drawableId) {
Drawable drawable = ContextCompat.getDrawable(context, drawableId);
if (drawable instanceof BitmapDrawable) {
return BitmapFactory.decodeResource(context.getResources(), drawableId);
} else if (drawable instanceof VectorDrawable) {
return getBitmap((VectorDrawable) drawable);
} else {
throw new IllegalArgumentException("unsupported drawable type");
}
}
private static Bitmap getBitmap(VectorDrawable vectorDrawable) {
Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(),
vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
vectorDrawable.draw(canvas);
return bitmap;
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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;
import com.m2049r.xmrwallet.service.MoneroHandlerThread;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class MoneroThreadPoolExecutor {
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(null, r, "MoneroTask #" + mCount.getAndIncrement(), MoneroHandlerThread.THREAD_STACK_SIZE);
}
};
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<>(128);
public static final Executor MONERO_THREAD_POOL_EXECUTOR;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
MONERO_THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
}

View File

@@ -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();
}
}

View File

@@ -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>

View File

@@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportHeight="75.0"
android:viewportWidth="75.0">
<path
android:fillColor="#ff6600"
android:pathData="M 37.3,0.35329395 c -20.377,0 -36.903,16.524 -36.903,36.902 0,4.074 0.66,7.992 1.88,11.657 l 11.036,0 0,-31.049 23.987,23.987 23.987,-23.987 0,31.049 11.037,0 c 1.22,-3.665 1.88,-7.583 1.88,-11.657 0,-20.378 -16.526,-36.902 -36.904,-36.902" />
<path
android:fillColor="#4c4c4c"
android:pathData="M 21.3164,36.895994 l 0,19.537 -15.55,0 c 6.478,10.628 18.178,17.726 31.533,17.726 13.355,0 25.056,-7.098 31.533,-17.726 l -15.549,0 0,-19.537 -15.984,15.984 z" />
</vector>

View File

@@ -0,0 +1,21 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="100dp"
android:height="100dp"
android:viewportHeight="75.0"
android:viewportWidth="75.0">
<path
android:fillColor="#ffffff"
android:pathData=" M 37.3, 37.3
m -36.9, 0
a 36.9,36.9 0 1,0 73.8,0
a 36.9,36.9 0 1,0 -73.8,0" />
<path
android:fillColor="#ff6600"
android:pathData="M 37.3,0.35329395 c -20.377,0 -36.903,16.524 -36.903,36.902 0,4.074 0.66,7.992 1.88,11.657 l 11.036,0 0,-31.049 23.987,23.987 23.987,-23.987 0,31.049 11.037,0 c 1.22,-3.665 1.88,-7.583 1.88,-11.657 0,-20.378 -16.526,-36.902 -36.904,-36.902" />
<path
android:fillColor="#4c4c4c"
android:pathData="M 21.3164,36.895994 l 0,19.537 -15.55,0 c 6.478,10.628 18.178,17.726 31.533,17.726 13.355,0 25.056,-7.098 31.533,-17.726 l -15.549,0 0,-19.537 -15.984,15.984 z" />
</vector>

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -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>

Some files were not shown because too many files have changed in this diff Show More