1
mirror of https://github.com/m2049r/xmrwallet synced 2025-09-04 00:53:36 +02:00

Compare commits

...

21 Commits

Author SHA1 Message Date
m2049r
347123d961 UI bugfixes (#120)
* better wallet load progress

new version

* dont start LoginFragment if is already started
2017-11-08 23:04:01 +01:00
m2049r
f5ad07c2b0 some bugfixes (#119)
* avoid IllegalStateException in rare cases

* clear error state on edit

* new version

* ignore error if fragment unloaded
2017-11-07 23:22:09 +01:00
m2049r
62695af9c6 layout correction + new version code 2017-11-05 01:01:36 +01:00
Stephan Hagios
3b1c3d564b Adjusted Layout files (#118)
* Adjusted layout files with correct sizes.

* Added password visibility toggle
2017-11-05 00:25:26 +01:00
Jonathan Cross
b28a140b48 FAQ improvements (#116) 2017-11-04 23:55:26 +01:00
m2049r
75bba4a091 remove e-notaion for android 5.0! (#117)
and new version
2017-11-04 23:54:49 +01:00
m2049r
cfb3c23003 hide receive button before wallet is loaded (#115) 2017-11-04 15:31:34 +01:00
m2049r
2cb87bab8e nodes help 2017-11-04 14:07:54 +01:00
m2049r
1ddc3d6b58 normal font for receiver 2017-11-04 13:45:21 +01:00
m2049r
2fa48d7441 more help & restyle reallysend button (#114) 2017-11-04 13:23:56 +01:00
m2049r
545367db90 Changes from Alpha Feedback (#113)
* fab covers whole screen

* avoid potential NPE

* bugfix - spinners crashed after scan

* Gunther only if no wallets

* clearQR on failed exchange, tweaks, gray

* refactor info dialogs

* added default testnet node

* FAQ: my testnet wallet is broken

* version code 36, version name 1.1.7
2017-11-04 10:17:20 +01:00
m2049r
d67e02cbcb Mostly Help "System" (#112)
* help & new exchange icon

* layout tweaks + removed nocopy icons

* sync help

* hide keyboard on spend

* QR Recieve => Receive

* menu tweaking

* use png logo for android < nougat

* gray generate

* position tweak in toolbar

* new exchange icon for wallet balance

* new About Dialog

* enable sending on mainnet

* new version 1.1.6

* dispose transaction on back

* remove unused onExchange method

* gray stuff + some tweaks

* new logo for qr
2017-11-04 00:43:24 +01:00
m2049r
d3beb7ca3f exchange & toolbar layout (#111) 2017-11-03 10:14:19 +01:00
m2049r
f951e1a621 abort exchange if amount is invalid 2017-11-03 01:20:59 +01:00
m2049r
0e187d3b20 version 2017-11-02 22:52:13 +01:00
m2049r
716b830b7b Tweaks based on alpha feedback (#110)
* donation+privacy policy screens + bugfixes

* opacity

* mnemonic left aligned

* smaller scan button

* spread out fabs

* doExchnage on lose focus of amount

* specify wallet create type
2017-11-02 22:48:20 +01:00
m2049r
4ac6a03d63 rename fresh wallet (no cachefile) (#109) 2017-11-02 11:46:03 +01:00
m2049r
7eb86ea618 tx details consistent with tx list (#107) 2017-11-01 20:33:58 +01:00
Stephan Hagios
4cfd166ed7 Adjusted visibility of fields and methods. (#102)
* Adjusted visibility of fields and methods.

* Adjusted visibility of fields and methods after rebase
2017-11-01 19:47:24 +01:00
m2049r
44f241f4ee fix progress dialog not showing (#106)
also, new version
2017-11-01 19:26:18 +01:00
m2049r
62b77fc987 no more animation because of OOMs (#105) 2017-11-01 10:56:47 +01:00
101 changed files with 2010 additions and 1572 deletions

1
.gitignore vendored
View File

@@ -7,3 +7,4 @@
/captures
.externalNativeBuild
.DS_Store
/app/release

View File

@@ -21,7 +21,6 @@ You may lose all your Moneroj if you use this App. Be cautious when spending on
- Monerujo means "Monero Wallet" according to https://www.reddit.com/r/Monero/comments/3exy7t/esperanto_corner/
### TODO
- review visibility of methods/classes
- more sensible error dialogs
### Issues / Pitfalls

View File

@@ -8,8 +8,8 @@ android {
applicationId "com.m2049r.xmrwallet"
minSdkVersion 21
targetSdkVersion 25
versionCode 29
versionName "1.1.2-alpha"
versionCode 44
versionName "1.2.3"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

View File

@@ -33,6 +33,7 @@ import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.TextView;
import com.m2049r.xmrwallet.layout.Toolbar;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.Helper;
@@ -48,16 +49,16 @@ public class GenerateFragment extends Fragment {
static final String TYPE_SEED = "seed";
static final String TYPE_VIEWONLY = "view";
TextInputLayout etWalletName;
TextInputLayout etWalletPassword;
TextInputLayout etWalletAddress;
TextInputLayout etWalletMnemonic;
TextInputLayout etWalletViewKey;
TextInputLayout etWalletSpendKey;
TextInputLayout etWalletRestoreHeight;
Button bGenerate;
private TextInputLayout etWalletName;
private TextInputLayout etWalletPassword;
private TextInputLayout etWalletAddress;
private TextInputLayout etWalletMnemonic;
private TextInputLayout etWalletViewKey;
private TextInputLayout etWalletSpendKey;
private TextInputLayout etWalletRestoreHeight;
private Button bGenerate;
String type = null;
private String type = null;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -366,7 +367,25 @@ public class GenerateFragment extends Fragment {
public void onResume() {
super.onResume();
Log.d(TAG, "onResume()");
activityCallback.setTitle(getString(R.string.generate_title));
activityCallback.setTitle(getString(R.string.generate_title) + " - " + getType());
activityCallback.setToolbarButton(Toolbar.BUTTON_BACK);
}
String getType() {
switch (type) {
case TYPE_KEY:
return getString(R.string.generate_wallet_type_key);
case TYPE_NEW:
return getString(R.string.generate_wallet_type_new);
case TYPE_SEED:
return getString(R.string.generate_wallet_type_seed);
case TYPE_VIEWONLY:
return getString(R.string.generate_wallet_type_view);
default:
Log.e(TAG, "unknown type " + type);
return "?";
}
}
GenerateFragment.Listener activityCallback;
@@ -379,6 +398,9 @@ public class GenerateFragment extends Fragment {
void onGenerate(String name, String password, String address, String viewKey, String spendKey, long height);
void setTitle(String title);
void setToolbarButton(int type);
}
@Override
@@ -400,7 +422,21 @@ public class GenerateFragment extends Fragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.create_wallet_menu, menu);
switch (type) {
case TYPE_KEY:
inflater.inflate(R.menu.create_wallet_keys, menu);
break;
case TYPE_NEW:
inflater.inflate(R.menu.create_wallet_new, menu);
break;
case TYPE_SEED:
inflater.inflate(R.menu.create_wallet_seed, menu);
break;
case TYPE_VIEWONLY:
inflater.inflate(R.menu.create_wallet_view, menu);
break;
default:
}
super.onCreateOptionsMenu(menu, inflater);
}
}

View File

@@ -103,18 +103,6 @@ public class GenerateReviewFragment extends Fragment {
copyAddress();
}
});
view.findViewById(R.id.bCopySeed).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
nocopy();
}
});
view.findViewById(R.id.bCopySepndKey).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
nocopy();
}
});
view.findViewById(R.id.bAdvancedInfo).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -305,5 +293,6 @@ public class GenerateReviewFragment extends Fragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.wallet_details_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
}

View File

@@ -47,8 +47,10 @@ import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.m2049r.xmrwallet.dialog.AboutFragment;
import com.m2049r.xmrwallet.dialog.HelpFragment;
import com.m2049r.xmrwallet.dialog.LicensesFragment;
import com.m2049r.xmrwallet.dialog.DonationFragment;
import com.m2049r.xmrwallet.dialog.PrivacyFragment;
import com.m2049r.xmrwallet.layout.Toolbar;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
@@ -73,9 +75,8 @@ public class LoginActivity extends AppCompatActivity
private static final String GENERATE_STACK = "gen";
static final int DAEMON_TIMEOUT = 500; // deamon must respond in 500ms
static final int DAEMON_DNS_TIMEOUT = 5000; // how long to wait for DNS resolver
Toolbar toolbar;
private Toolbar toolbar;
@Override
public void setToolbarButton(int type) {
@@ -121,7 +122,8 @@ public class LoginActivity extends AppCompatActivity
finish();
break;
case Toolbar.BUTTON_DONATE:
Toast.makeText(LoginActivity.this, getString(R.string.label_donate), Toast.LENGTH_SHORT).show();
DonationFragment.display(getSupportFragmentManager());
break;
case Toolbar.BUTTON_NONE:
default:
Log.e(TAG, "Button " + type + "pressed - how can this be?");
@@ -130,7 +132,7 @@ public class LoginActivity extends AppCompatActivity
});
if (Helper.getWritePermission(this)) {
startLoginFragment();
if (savedInstanceState == null) startLoginFragment();
} else {
Log.i(TAG, "Waiting for permissions");
}
@@ -251,7 +253,7 @@ public class LoginActivity extends AppCompatActivity
// copy + delete seems safer than rename because we call rollback easily
boolean renameWallet(File walletFile, String newName) {
if (copyWallet(walletFile, new File(walletFile.getParentFile(), newName), false)) {
if (copyWallet(walletFile, new File(walletFile.getParentFile(), newName), false, true)) {
deleteWallet(walletFile);
return true;
} else {
@@ -356,7 +358,7 @@ public class LoginActivity extends AppCompatActivity
// TODO probably better to copy to a new file and then rename
// then if something fails we have the old backup at least
// or just create a new backup every time and keep n old backups
boolean success = copyWallet(walletFile, backupFile, true);
boolean success = copyWallet(walletFile, backupFile, true, true);
Log.d(TAG, "copyWallet is " + success);
return success;
}
@@ -604,6 +606,8 @@ public class LoginActivity extends AppCompatActivity
if (progressDialog != null) progressDialog.show();
}
}, delay);
} else {
progressDialog.show();
}
}
@@ -969,8 +973,8 @@ public class LoginActivity extends AppCompatActivity
}
}
boolean copyWallet(File srcWallet, File dstWallet, boolean backupMode) {
if (walletExists(dstWallet, true) && !backupMode) return false;
boolean copyWallet(File srcWallet, File dstWallet, boolean overwrite, boolean ignoreCacheError) {
if (walletExists(dstWallet, true) && !overwrite) return false;
boolean success = false;
File srcDir = srcWallet.getParentFile();
String srcName = srcWallet.getName();
@@ -980,8 +984,8 @@ public class LoginActivity extends AppCompatActivity
try {
copyFile(new File(srcDir, srcName), new File(dstDir, dstName));
} catch (IOException ex) {
Log.d(TAG, "CACHE " + backupMode);
if (!backupMode) { // ignore cache backup error if backing up (can be resynced)
Log.d(TAG, "CACHE " + ignoreCacheError);
if (!ignoreCacheError) { // ignore cache backup error if backing up (can be resynced)
throw ex;
}
}
@@ -1049,14 +1053,29 @@ public class LoginActivity extends AppCompatActivity
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_create_help:
HelpFragment.displayHelp(getSupportFragmentManager(), R.raw.help_create);
case R.id.action_create_help_new:
HelpFragment.display(getSupportFragmentManager(), R.string.help_create_new);
return true;
case R.id.action_create_help_keys:
HelpFragment.display(getSupportFragmentManager(), R.string.help_create_keys);
return true;
case R.id.action_create_help_view:
HelpFragment.display(getSupportFragmentManager(), R.string.help_create_view);
return true;
case R.id.action_create_help_seed:
HelpFragment.display(getSupportFragmentManager(), R.string.help_create_seed);
return true;
case R.id.action_details_help:
HelpFragment.displayHelp(getSupportFragmentManager(), R.raw.help_details);
HelpFragment.display(getSupportFragmentManager(), R.string.help_details);
return true;
case R.id.action_lincense_info:
LicensesFragment.displayLicensesFragment(getSupportFragmentManager());
case R.id.action_license_info:
AboutFragment.display(getSupportFragmentManager());
return true;
case R.id.action_help_list:
HelpFragment.display(getSupportFragmentManager(), R.string.help_list);
return true;
case R.id.action_privacy_policy:
PrivacyFragment.display(getSupportFragmentManager());
return true;
case R.id.action_testnet:
try {

View File

@@ -18,9 +18,7 @@ package com.m2049r.xmrwallet;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
@@ -66,8 +64,8 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
List<WalletManager.WalletInfo> walletList = new ArrayList<>();
List<WalletManager.WalletInfo> displayedList = new ArrayList<>();
ImageView ivGuntherWallets;
EditText etDummy;
ImageView ivGunther;
DropDownEditText etDaemonAddress;
ArrayAdapter<String> nodeAdapter;
@@ -134,8 +132,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
Log.d(TAG, "onCreateView");
View view = inflater.inflate(R.layout.fragment_login, container, false);
ivGuntherWallets = (ImageView) view.findViewById(R.id.ivGuntherWallets);
ivGunther = (ImageView) view.findViewById(R.id.ivGunther);
fabScreen = (FrameLayout) view.findViewById(R.id.fabScreen);
fab = (FloatingActionButton) view.findViewById(R.id.fab);
fabNew = (FloatingActionButton) view.findViewById(R.id.fabNew);
@@ -148,6 +145,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
fabKeyL = (RelativeLayout) view.findViewById(R.id.fabKeyL);
fabSeedL = (RelativeLayout) view.findViewById(R.id.fabSeedL);
fab_pulse = AnimationUtils.loadAnimation(getContext(), R.anim.fab_pulse);
fab_open_screen = AnimationUtils.loadAnimation(getContext(), R.anim.fab_open_screen);
fab_close_screen = AnimationUtils.loadAnimation(getContext(), R.anim.fab_close_screen);
fab_open = AnimationUtils.loadAnimation(getContext(), R.anim.fab_open);
@@ -213,25 +211,10 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
});
loadPrefs();
return view;
}
void showGunther() {
ivGuntherWallets.setImageResource(R.drawable.gunther_wallets);
final AnimationDrawable guntherWalletsAnim = (AnimationDrawable) ivGuntherWallets.getDrawable();
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
guntherWalletsAnim.start();
}
}, getResources().getInteger(R.integer.gunther_wallets_delay));
}
void normalGunther() {
ivGuntherWallets.setImageResource(R.drawable.gunther_wallets_00);
}
// Callbacks from WalletInfoAdapter
@Override
public void onInteraction(final View view, final WalletManager.WalletInfo infoItem) {
@@ -288,10 +271,18 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
filterList();
adapter.setInfos(displayedList);
adapter.notifyDataSetChanged();
// deal with Gunther & FAB animation
if (displayedList.isEmpty()) {
showGunther();
fab.startAnimation(fab_pulse);
if (ivGunther.getDrawable() == null) {
ivGunther.setImageResource(R.drawable.gunther_desaturated);
}
} else {
normalGunther();
fab.clearAnimation();
if (ivGunther.getDrawable() != null) {
ivGunther.setImageDrawable(null);
}
}
}
@@ -348,7 +339,10 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
//private static final String PREF_TESTNET = "testnet";
private static final String PREF_DAEMONLIST_MAINNET =
"node.moneroworld.com:18089;node.xmrbackb.one:18081;node.xmr.be:18081";
"node.moneroworld.com:18089;node.xmrbackb.one;node.xmr.be";
private static final String PREF_DAEMONLIST_TESTNET =
"testnet.xmrchain.net";
private NodeList daemonTestNet;
private NodeList daemonMainNet;
@@ -357,7 +351,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
SharedPreferences sharedPref = activityCallback.getPrefs();
daemonMainNet = new NodeList(sharedPref.getString(PREF_DAEMON_MAINNET, PREF_DAEMONLIST_MAINNET));
daemonTestNet = new NodeList(sharedPref.getString(PREF_DAEMON_TESTNET, ""));
daemonTestNet = new NodeList(sharedPref.getString(PREF_DAEMON_TESTNET, PREF_DAEMONLIST_TESTNET));
setNet(isTestnet(), false);
}
@@ -407,6 +401,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
private FrameLayout fabScreen;
private RelativeLayout fabNewL, fabViewL, fabKeyL, fabSeedL;
private Animation fab_open, fab_close, rotate_forward, rotate_backward, fab_open_screen, fab_close_screen;
private Animation fab_pulse;
public boolean isFabOpen() {
return isFabOpen;

View File

@@ -59,15 +59,15 @@ import java.util.Map;
public class ReceiveFragment extends Fragment {
static final String TAG = "ReceiveFragment";
ProgressBar pbProgress;
TextView tvAddress;
TextInputLayout etPaymentId;
ExchangeView evAmount;
Button bPaymentId;
Button bGenerate;
ImageView qrCode;
EditText etDummy;
ImageButton bCopyAddress;
private ProgressBar pbProgress;
private TextView tvAddress;
private TextInputLayout etPaymentId;
private ExchangeView evAmount;
private Button bPaymentId;
private Button bGenerate;
private ImageView qrCode;
private EditText etDummy;
private ImageButton bCopyAddress;
//String name;
@@ -112,21 +112,17 @@ public class ReceiveFragment extends Fragment {
generateQr();
}
});
/*
evAmount.setOnAmountInvalidatedListener(new ExchangeView.OnAmountInvalidatedListener() {
@Override
public void onAmountInvalidated() {
clearQR();
}
});
evAmount.setOnFailedExchangeListener(new ExchangeView.OnFailedExchangeListener() {
@Override
public void onFailedExchange() {
Toast.makeText(getActivity(), getString(R.string.message_exchange_failed), Toast.LENGTH_LONG).show();
if (isAdded()) {
clearQR();
Toast.makeText(getActivity(), getString(R.string.message_exchange_failed), Toast.LENGTH_LONG).show();
}
}
});
*/
etPaymentId.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) {
@@ -373,7 +369,7 @@ public class ReceiveFragment extends Fragment {
private Bitmap getMoneroLogo() {
if (logo == null) {
logo = Helper.getBitmap(getContext(), R.drawable.ic_monero_qr);
logo = Helper.getBitmap(getContext(), R.drawable.ic_monero_logo_b);
}
return logo;
}

View File

@@ -34,7 +34,7 @@ import me.dm7.barcodescanner.zxing.ZXingScannerView;
public class ScannerFragment extends Fragment implements ZXingScannerView.ResultHandler {
static final String TAG = "ScannerFragment";
Listener activityCallback;
private Listener activityCallback;
public interface Listener {
boolean onAddressScanned(String uri);

View File

@@ -20,16 +20,22 @@ import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.design.widget.TextInputLayout;
import android.support.v4.app.Fragment;
import android.support.v7.app.AlertDialog;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
@@ -43,7 +49,6 @@ import com.m2049r.xmrwallet.layout.Toolbar;
import com.m2049r.xmrwallet.model.PendingTransaction;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.AsyncExchangeRate;
import com.m2049r.xmrwallet.util.BarcodeData;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.TxData;
@@ -51,33 +56,30 @@ import com.m2049r.xmrwallet.util.TxData;
public class SendFragment extends Fragment {
static final String TAG = "SendFragment";
EditText etDummy;
private EditText etDummy;
ScrollView scrollview;
private ScrollView scrollview;
TextInputLayout etAddress;
TextInputLayout etPaymentId;
//TextInputLayout etAmount;
ExchangeView evAmount;
TextView tvAmountB;
Spinner sCurrencyA;
Spinner sCurrencyB;
private TextInputLayout etAddress;
private TextInputLayout etPaymentId;
Button bScan;
Spinner sMixin;
Spinner sPriority;
Button bPrepareSend;
Button bDispose;
Button bPaymentId;
LinearLayout llConfirmSend;
TextView tvTxAmount;
TextView tvTxFee;
private ExchangeView evAmount;
private Button bScan;
private Spinner sMixin;
private Spinner sPriority;
private Button bPrepareSend;
private Button bDispose;
private Button bPaymentId;
private LinearLayout llConfirmSend;
private TextView tvTxAmount;
private TextView tvTxFee;
//TextView tvTxDust;
TextView tvTxTotal;
EditText etNotes;
Button bSend;
Button bReallySend;
ProgressBar pbProgress;
private TextView tvTxTotal;
private EditText etNotes;
private Button bSend;
private Button bReallySend;
private ProgressBar pbProgress;
final static int Mixins[] = {4, 7, 12, 25}; // must match the layout XML
final static PendingTransaction.Priority Priorities[] =
@@ -102,9 +104,6 @@ public class SendFragment extends Fragment {
etAddress = (TextInputLayout) view.findViewById(R.id.etAddress);
etPaymentId = (TextInputLayout) view.findViewById(R.id.etPaymentId);
evAmount = (ExchangeView) view.findViewById(R.id.evAmount);
tvAmountB = (TextView) view.findViewById(R.id.tvAmountB);
sCurrencyA = (Spinner) view.findViewById(R.id.sCurrencyA);
sCurrencyB = (Spinner) view.findViewById(R.id.sCurrencyB);
bScan = (Button) view.findViewById(R.id.bScan);
bPrepareSend = (Button) view.findViewById(R.id.bPrepareSend);
@@ -140,15 +139,18 @@ public class SendFragment extends Fragment {
}
});
evAmount.setOnNewAmountListener(new ExchangeView.OnNewAmountListener() {
etAddress.getEditText().addTextChangedListener(new TextWatcher() {
@Override
public void onNewAmount(String xmr) {
if ((xmr != null)) {
// stupid workaround to not show error on open of screen
if ((checkAddressNoError() && checkAmountWithError()) || checkAmount()) {
etPaymentId.requestFocus();
}
}
public void afterTextChanged(Editable editable) {
etAddress.setError(null);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
});
@@ -165,6 +167,21 @@ public class SendFragment extends Fragment {
}
});
etPaymentId.getEditText().addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable editable) {
etPaymentId.setError(null);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
});
bPrepareSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -213,8 +230,8 @@ public class SendFragment extends Fragment {
public void onClick(View v) {
bSend.setEnabled(false);
boolean testnet = WalletManager.getInstance().isTestNet();
if (!testnet) {
//send();
if (testnet) {
send();
} else {
etNotes.setEnabled(false);
Handler handler = new Handler();
@@ -243,6 +260,42 @@ public class SendFragment extends Fragment {
}
});
sMixin.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(final AdapterView<?> parentView, View selectedItemView, int position, long id) {
parentView.post(new Runnable() {
@Override
public void run() {
if (isAdded())
((TextView) parentView.getChildAt(0)).setTextColor(getResources().getColor(R.color.moneroGray));
}
});
}
@Override
public void onNothingSelected(AdapterView<?> parentView) {
// nothing (yet?)
}
});
sPriority.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(final AdapterView<?> parentView, View selectedItemView, int position, long id) {
parentView.post(new Runnable() {
@Override
public void run() {
if (isAdded())
((TextView) parentView.getChildAt(0)).setTextColor(getResources().getColor(R.color.moneroGray));
}
});
}
@Override
public void onNothingSelected(AdapterView<?> parentView) {
// nothing (yet?)
}
});
etDummy.requestFocus();
Helper.hideKeyboard(getActivity());
@@ -365,8 +418,6 @@ public class SendFragment extends Fragment {
BarcodeData popScannedData();
void onExchange(AsyncExchangeRate.Listener listener, String currencyA, String currencyB);
void setSubtitle(String subtitle);
void setToolbarButton(int type);
@@ -469,4 +520,15 @@ public class SendFragment extends Fragment {
pbProgress.setVisibility(View.GONE);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.send_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
}

View File

@@ -57,17 +57,17 @@ public class TxFragment extends Fragment {
TS_FORMATTER.setTimeZone(tz);
}
TextView tvTxTimestamp;
TextView tvTxId;
TextView tvTxKey;
TextView tvDestination;
TextView tvTxPaymentId;
TextView tvTxBlockheight;
TextView tvTxAmount;
TextView tvTxFee;
TextView tvTxTransfers;
TextView etTxNotes;
Button bTxNotes;
private TextView tvTxTimestamp;
private TextView tvTxId;
private TextView tvTxKey;
private TextView tvDestination;
private TextView tvTxPaymentId;
private TextView tvTxBlockheight;
private TextView tvTxAmount;
private TextView tvTxFee;
private TextView tvTxTransfers;
private TextView etTxNotes;
private Button bTxNotes;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -212,18 +212,20 @@ public class TxFragment extends Fragment {
}
String sign = (info.direction == TransactionInfo.Direction.Direction_In ? "+" : "-");
tvTxAmount.setText(sign + Wallet.getDisplayAmount(info.amount));
long realAmount = info.amount;
if (info.isPending) {
realAmount = realAmount - info.fee;
}
tvTxAmount.setText(sign + Wallet.getDisplayAmount(realAmount));
if ((info.fee > 0)) {
String fee = Wallet.getDisplayAmount(info.fee);
if (info.isPending) {
tvTxFee.setText(getString(R.string.tx_list_fee_pending, fee));
} else {
tvTxFee.setText(getString(R.string.tx_list_fee, fee));
}
tvTxFee.setText(getString(R.string.tx_list_fee, fee));
} else {
tvTxFee.setText(null);
tvTxFee.setVisibility(View.GONE);
}
if (info.isFailed) {
tvTxAmount.setText(getString(R.string.tx_list_amount_failed, Wallet.getDisplayAmount(info.amount)));
tvTxFee.setText(getString(R.string.tx_list_failed_text));

View File

@@ -36,13 +36,14 @@ import android.util.Log;
import android.view.MenuItem;
import android.widget.Toast;
import com.m2049r.xmrwallet.dialog.DonationFragment;
import com.m2049r.xmrwallet.dialog.HelpFragment;
import com.m2049r.xmrwallet.layout.Toolbar;
import com.m2049r.xmrwallet.model.PendingTransaction;
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.AsyncExchangeRate;
import com.m2049r.xmrwallet.util.BarcodeData;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.TxData;
@@ -60,7 +61,7 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
public static final String REQUEST_ID = "id";
public static final String REQUEST_PW = "pw";
Toolbar toolbar;
private Toolbar toolbar;
@Override
public void setToolbarButton(int type) {
@@ -154,11 +155,23 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
onWalletDetails();
return true;
case R.id.action_donate:
onWalletDetails();
DonationFragment.display(getSupportFragmentManager());
return true;
case R.id.action_share:
onShareTxInfo();
return true;
case R.id.action_help_tx_info:
HelpFragment.display(getSupportFragmentManager(), R.string.help_tx_details);
return true;
case R.id.action_help_wallet:
HelpFragment.display(getSupportFragmentManager(), R.string.help_wallet);
return true;
case R.id.action_details_help:
HelpFragment.display(getSupportFragmentManager(), R.string.help_details);
return true;
case R.id.action_help_send:
HelpFragment.display(getSupportFragmentManager(), R.string.help_send);
return true;
default:
return super.onOptionsItemSelected(item);
}
@@ -186,6 +199,7 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
public void onButton(int type) {
switch (type) {
case Toolbar.BUTTON_BACK:
onDisposeRequest();
onBackPressed();
break;
case Toolbar.BUTTON_CLOSE:
@@ -209,7 +223,7 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
Fragment walletFragment = new WalletFragment();
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, walletFragment).commit();
.add(R.id.fragment_container, walletFragment, WalletFragment.TAG).commit();
Log.d(TAG, "fragment added");
startWalletService();
@@ -375,12 +389,12 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
Log.d(TAG, "onRefreshed()");
try {
final WalletFragment walletFragment = (WalletFragment)
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
getSupportFragmentManager().findFragmentByTag(WalletFragment.TAG);
if (wallet.isSynchronized()) {
Log.d(TAG, "onRefreshed() synced");
releaseWakeLock(); // the idea is to stay awake until synced
if (!synced) {
onProgress(null);
if (!synced) { // first sync
onProgress(-1);
saveWallet(); // save on first sync
synced = true;
runOnUiThread(new Runnable() {
@@ -433,6 +447,16 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
} else {
haveWallet = true;
invalidateOptionsMenu();
final WalletFragment walletFragment = (WalletFragment)
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
runOnUiThread(new Runnable() {
public void run() {
if (walletFragment != null) {
walletFragment.onLoaded();
}
}
});
}
}
@@ -499,10 +523,10 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
public void onProgress(final String text) {
try {
final WalletFragment walletFragment = (WalletFragment)
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
getSupportFragmentManager().findFragmentByTag(WalletFragment.TAG);
runOnUiThread(new Runnable() {
public void run() {
walletFragment.onProgress(text);
walletFragment.setProgress(text);
}
});
} catch (ClassCastException ex) {
@@ -516,10 +540,10 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
public void onProgress(final int n) {
try {
final WalletFragment walletFragment = (WalletFragment)
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
getSupportFragmentManager().findFragmentByTag(WalletFragment.TAG);
runOnUiThread(new Runnable() {
public void run() {
walletFragment.onProgress(n);
walletFragment.setProgress(n);
}
});
} catch (ClassCastException ex) {
@@ -781,9 +805,4 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
replaceFragment(new ReceiveFragment(), null, extras);
Log.d(TAG, "ReceiveFragment placed");
}
@Override
public void onExchange(AsyncExchangeRate.Listener listener, String currencyA, String currencyB) {
new AsyncExchangeRate(listener).execute(currencyA, currencyB);
}
}

View File

@@ -49,20 +49,20 @@ import java.util.List;
public class WalletFragment extends Fragment
implements TransactionInfoAdapter.OnInteractionListener,
AsyncExchangeRate.Listener {
private static final String TAG = "WalletFragment";
public static final String TAG = "WalletFragment";
private TransactionInfoAdapter adapter;
private NumberFormat formatter = NumberFormat.getInstance();
FrameLayout flExchange;
TextView tvBalance;
TextView tvUnconfirmedAmount;
TextView tvProgress;
ImageView ivSynced;
ProgressBar pbProgress;
Button bReceive;
Button bSend;
private FrameLayout flExchange;
private TextView tvBalance;
private TextView tvUnconfirmedAmount;
private TextView tvProgress;
private ImageView ivSynced;
private ProgressBar pbProgress;
private Button bReceive;
private Button bSend;
Spinner sCurrency;
private Spinner sCurrency;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
@@ -96,7 +96,7 @@ public class WalletFragment extends Fragment
ivSynced = (ImageView) view.findViewById(R.id.ivSynced);
sCurrency = (Spinner) view.findViewById(R.id.sCurrency);
sCurrency.setAdapter(ArrayAdapter.createFromResource(getContext(), R.array.currency, R.layout.item_spinner));
sCurrency.setAdapter(ArrayAdapter.createFromResource(getContext(), R.array.currency, R.layout.item_spinner_balance));
bSend = (Button) view.findViewById(R.id.bSend);
bReceive = (Button) view.findViewById(R.id.bReceive);
@@ -244,27 +244,40 @@ public class WalletFragment extends Fragment
}
}
public void setProgressText(final String text) {
tvProgress.setText(text);
boolean walletLoaded = false;
public void onLoaded() {
walletLoaded = true;
showReceive();
}
public void onProgress(final String text) {
if (text != null) {
setProgressText(text);
pbProgress.setVisibility(View.VISIBLE);
} else {
pbProgress.setVisibility(View.INVISIBLE);
setProgressText(getString(R.string.status_working));
onProgress(-1);
private void showReceive() {
if (walletLoaded) {
bReceive.setVisibility(View.VISIBLE);
bReceive.setEnabled(true);
}
}
public void onProgress(final int n) {
if (n >= 0) {
private String syncText = null;
public void setProgress(final String text) {
syncText = text;
tvProgress.setText(text);
}
private int syncProgress = -1;
public void setProgress(final int n) {
syncProgress = n;
if (n > 100) {
pbProgress.setIndeterminate(true);
pbProgress.setVisibility(View.VISIBLE);
} else if (n >= 0) {
pbProgress.setIndeterminate(false);
pbProgress.setProgress(n);
} else {
pbProgress.setIndeterminate(true);
pbProgress.setVisibility(View.VISIBLE);
} else { // <0
pbProgress.setVisibility(View.INVISIBLE);
}
}
@@ -287,7 +300,6 @@ public class WalletFragment extends Fragment
Log.d(TAG, "updateStatus()");
if (walletTitle == null) {
setActivityTitle(wallet);
onProgress(100); // of loading
}
long balance = wallet.getBalance();
unlockedBalance = wallet.getUnlockedBalance();
@@ -295,8 +307,6 @@ public class WalletFragment extends Fragment
double amountXmr = Double.parseDouble(Helper.getDisplayAmount(balance - unlockedBalance)); // assume this cannot fail!
String unconfirmed = Helper.getFormattedAmount(amountXmr, true);
tvUnconfirmedAmount.setText(getResources().getString(R.string.xmr_unconfirmed_amount, unconfirmed));
//tvUnconfirmedAmount.setText(getResources().getString(R.string.xmr_unconfirmed_amount,
// Helper.getDisplayAmount(balance - unlockedBalance, Helper.DISPLAY_DIGITS_SHORT)));
String sync = "";
if (!activityCallback.hasBoundService())
throw new IllegalStateException("WalletService not bound.");
@@ -305,21 +315,23 @@ public class WalletFragment extends Fragment
long daemonHeight = activityCallback.getDaemonHeight();
if (!wallet.isSynchronized()) {
long n = daemonHeight - wallet.getBlockChainHeight();
sync = formatter.format(n) + " " + getString(R.string.status_remaining);
sync = getString(R.string.status_syncing) + " " + formatter.format(n) + " " + getString(R.string.status_remaining);
if (firstBlock == 0) {
firstBlock = wallet.getBlockChainHeight();
}
int x = 100 - Math.round(100f * n / (1f * daemonHeight - firstBlock));
onProgress(getString(R.string.status_syncing) + " " + sync);
if (x == 0) x = -1;
onProgress(x);
if (x == 0) x = 101; // indeterminate
setProgress(x);
ivSynced.setVisibility(View.GONE);
} else {
sync = getString(R.string.status_synced) + ": " + formatter.format(wallet.getBlockChainHeight());
sync = getString(R.string.status_synced) + formatter.format(wallet.getBlockChainHeight());
ivSynced.setVisibility(View.VISIBLE);
}
} else {
sync = getString(R.string.status_wallet_connecting);
setProgress(101);
}
setProgressText(sync);
setProgress(sync);
// TODO show connected status somewhere
}
@@ -373,5 +385,8 @@ public class WalletFragment extends Fragment
Log.d(TAG, "onResume()");
activityCallback.setTitle(walletTitle, walletSubtitle);
activityCallback.setToolbarButton(Toolbar.BUTTON_CLOSE);
setProgress(syncProgress);
setProgress(syncText);
showReceive();
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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.dialog;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import com.m2049r.xmrwallet.BuildConfig;
import com.m2049r.xmrwallet.R;
public class AboutFragment extends DialogFragment {
static final String TAG = "AboutFragment";
public static AboutFragment newInstance() {
return new AboutFragment();
}
public static void display(FragmentManager fm) {
FragmentTransaction ft = fm.beginTransaction();
Fragment prev = fm.findFragmentByTag(TAG);
if (prev != null) {
ft.remove(prev);
}
AboutFragment.newInstance().show(ft, TAG);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_about, null);
((TextView) view.findViewById(R.id.tvHelp)).setText(Html.fromHtml(getString(R.string.about_licenses)));
((TextView) view.findViewById(R.id.tvVersion)).setText(getString(R.string.about_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(view);
builder.setNegativeButton(R.string.about_close,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
dialog.dismiss();
}
});
return builder.create();
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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.dialog;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.util.Helper;
public class DonationFragment extends DialogFragment {
static final String TAG = "DonationFragment";
public static DonationFragment newInstance() {
return new DonationFragment();
}
public static void display(FragmentManager fm) {
FragmentTransaction ft = fm.beginTransaction();
Fragment prev = fm.findFragmentByTag(TAG);
if (prev != null) {
ft.remove(prev);
}
DonationFragment.newInstance().show(ft, TAG);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_donation, null);
((TextView) view.findViewById(R.id.tvCredits)).setText(Html.fromHtml(getString(R.string.donation_credits)));
(view.findViewById(R.id.bCopyAddress)).
setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_address),
((TextView) view.findViewById(R.id.tvWalletAddress)).getText().toString());
Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show();
}
});
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(view);
builder.setNegativeButton(R.string.about_close,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
dialog.dismiss();
}
});
return builder.create();
}
}

View File

@@ -1,12 +1,12 @@
/**
* Copyright 2013 Adam Speakman, m2049r
* <p>
/*
* 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
* <p>
*
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
*
* 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.
@@ -16,98 +16,57 @@
package com.m2049r.xmrwallet.dialog;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.webkit.WebView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.m2049r.xmrwallet.R;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* Based on LicensesFragment by Adam Speakman on 24/09/13.
* http://speakman.net.nz
*/
public class HelpFragment extends DialogFragment {
static final String TAG = "HelpFragment";
private static final String FRAGMENT_TAG = "com.m2049r.xmrwallet.dialog.HelpFragment";
private static final String HELP_ID = "HELP_ID";
private AsyncTask<Void, Void, String> loader;
public static HelpFragment newInstance(int helpResourceId) {
HelpFragment fragment = new HelpFragment();
Bundle bundle = new Bundle();
bundle.putInt(HELP_ID, helpResourceId);
fragment.setArguments(bundle);
return fragment;
}
/**
* @param fm A fragment manager instance used to display this LicensesFragment.
*/
public static void displayHelp(FragmentManager fm, int helpResourceId) {
public static void display(FragmentManager fm, int helpResourceId) {
FragmentTransaction ft = fm.beginTransaction();
Fragment prev = fm.findFragmentByTag(FRAGMENT_TAG);
Fragment prev = fm.findFragmentByTag(TAG);
if (prev != null) {
ft.remove(prev);
}
// Create and show the dialog.
DialogFragment newFragment = HelpFragment.newInstance(helpResourceId);
newFragment.show(ft, FRAGMENT_TAG);
HelpFragment.newInstance(helpResourceId).show(ft, TAG);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
public Dialog onCreateDialog(Bundle savedInstanceState) {
final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_help, null);
int helpId = 0;
Bundle arguments = getArguments();
if (arguments != null) {
helpId = arguments.getInt(HELP_ID);
}
if (helpId > 0)
loadHelp(helpId);
}
@Override
public void onDestroy() {
super.onDestroy();
if (loader != null) {
loader.cancel(true);
}
}
private WebView webView;
private ProgressBar progress;
@SuppressLint("InflateParams")
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
View content = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_help, null);
webView = (WebView) content.findViewById(R.id.helpFragmentWebView);
progress = (ProgressBar) content.findViewById(R.id.helpFragmentProgress);
((TextView) view.findViewById(R.id.tvHelp)).setText(Html.fromHtml(getString(helpId)));
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(content);
builder.setView(view);
builder.setNegativeButton(R.string.about_close,
new DialogInterface.OnClickListener() {
@Override
@@ -115,47 +74,6 @@ public class HelpFragment extends DialogFragment {
dialog.dismiss();
}
});
return builder.create();
}
private void loadHelp(final int helpResourceId) {
// Load asynchronously in case of a very large file.
loader = new AsyncTask<Void, Void, String>() {
@Override
protected String doInBackground(Void... params) {
InputStream rawResource = getActivity().getResources().openRawResource(helpResourceId);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(rawResource));
String line;
StringBuilder sb = new StringBuilder();
try {
while ((line = bufferedReader.readLine()) != null) {
sb.append(line);
sb.append("\n");
}
bufferedReader.close();
} catch (IOException ex) {
Log.e(TAG, ex.getLocalizedMessage());
}
return sb.toString();
}
@Override
protected void onPostExecute(String licensesBody) {
super.onPostExecute(licensesBody);
if (getActivity() == null || isCancelled()) {
return;
}
progress.setVisibility(View.INVISIBLE);
webView.setVisibility(View.VISIBLE);
webView.loadDataWithBaseURL(null, licensesBody, "text/html", "utf-8", null);
loader = null;
}
}.execute();
}
}

View File

@@ -1,165 +0,0 @@
/**
* Copyright 2013 Adam Speakman, m2049r
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.dialog;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.webkit.WebView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.m2049r.xmrwallet.BuildConfig;
import com.m2049r.xmrwallet.R;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* Created by Adam Speakman on 24/09/13.
* http://speakman.net.nz
*/
public class LicensesFragment extends DialogFragment {
static final String TAG = "LicensesFragment";
int versionCode = BuildConfig.VERSION_CODE;
String versionName = BuildConfig.VERSION_NAME;
private AsyncTask<Void, Void, String> mLicenseLoader;
private static final String FRAGMENT_TAG = "com.m2049r.xmrwalelt.dialog.LicensesFragment";
/**
* Creates a new instance of LicensesFragment with no Close button.
*
* @return A new licenses fragment.
*/
public static LicensesFragment newInstance() {
return new LicensesFragment();
}
/**
* Builds and displays a licenses fragment with no Close button. Requires
* "/res/raw/licenses.html" and "/res/layout/licenses_fragment.xml" to be
* present.
*
* @param fm A fragment manager instance used to display this LicensesFragment.
*/
public static void displayLicensesFragment(FragmentManager fm) {
FragmentTransaction ft = fm.beginTransaction();
Fragment prev = fm.findFragmentByTag(FRAGMENT_TAG);
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack(null);
// Create and show the dialog.
DialogFragment newFragment = LicensesFragment.newInstance();
newFragment.show(ft, FRAGMENT_TAG);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
loadLicenses();
}
@Override
public void onDestroy() {
super.onDestroy();
if (mLicenseLoader != null) {
mLicenseLoader.cancel(true);
}
}
private WebView mWebView;
private ProgressBar mIndeterminateProgress;
@SuppressLint("InflateParams")
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
View content = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_licenses, null);
mWebView = (WebView) content.findViewById(R.id.licensesFragmentWebView);
mIndeterminateProgress = (ProgressBar) content.findViewById(R.id.licensesFragmentIndeterminateProgress);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
TextView text = (TextView) content.findViewById(R.id.licensesFragmentText);
text.setText(getString(R.string.about_text, versionName, versionCode));
builder.setView(content);
builder.setNegativeButton(R.string.about_close,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
dialog.dismiss();
}
});
return builder.create();
}
private void loadLicenses() {
// Load asynchronously in case of a very large file.
mLicenseLoader = new AsyncTask<Void, Void, String>() {
@Override
protected String doInBackground(Void... params) {
InputStream rawResource = getActivity().getResources().openRawResource(R.raw.licenses);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(rawResource));
String line;
StringBuilder sb = new StringBuilder();
try {
while ((line = bufferedReader.readLine()) != null) {
sb.append(line);
sb.append("\n");
}
bufferedReader.close();
} catch (IOException ex) {
Log.e(TAG, ex.getLocalizedMessage());
}
return sb.toString();
}
@Override
protected void onPostExecute(String licensesBody) {
super.onPostExecute(licensesBody);
if (getActivity() == null || isCancelled()) {
return;
}
mIndeterminateProgress.setVisibility(View.INVISIBLE);
mWebView.setVisibility(View.VISIBLE);
mWebView.loadDataWithBaseURL(null, licensesBody, "text/html", "utf-8", null);
mLicenseLoader = null;
}
}.execute();
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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.dialog;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import com.m2049r.xmrwallet.R;
public class PrivacyFragment extends DialogFragment {
static final String TAG = "PrivacyFragment";
public static PrivacyFragment newInstance() {
return new PrivacyFragment();
}
public static void display(FragmentManager fm) {
FragmentTransaction ft = fm.beginTransaction();
Fragment prev = fm.findFragmentByTag(TAG);
if (prev != null) {
ft.remove(prev);
}
PrivacyFragment.newInstance().show(ft, TAG);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_privacy_policy, null);
((TextView) view.findViewById(R.id.tvCredits)).setText(Html.fromHtml(getString(R.string.privacy_policy)));
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(view);
builder.setNegativeButton(R.string.about_close,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
dialog.dismiss();
}
});
return builder.create();
}
}

View File

@@ -67,6 +67,7 @@ public class ExchangeView extends LinearLayout implements AsyncExchangeRate.List
public void setAmount(String xmrAmount) {
if (xmrAmount != null) {
setCurrencyA(0);
etAmount.getEditText().setText(xmrAmount);
setXmr(xmrAmount);
this.notXmrAmount = null;
doExchange();
@@ -178,10 +179,16 @@ public class ExchangeView extends LinearLayout implements AsyncExchangeRate.List
sCurrencyB.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
public void onItemSelected(final AdapterView<?> parentView, View selectedItemView, int position, long id) {
if (position != 0) { // if not XMR, select XMR on other
sCurrencyA.setSelection(0, true);
}
parentView.post(new Runnable() {
@Override
public void run() {
((TextView) parentView.getChildAt(0)).setTextColor(getResources().getColor(R.color.moneroGray));
}
});
doExchange();
}
@@ -191,6 +198,15 @@ public class ExchangeView extends LinearLayout implements AsyncExchangeRate.List
}
});
etAmount.getEditText().setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
doExchange();
}
}
});
etAmount.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) {
@@ -255,10 +271,6 @@ public class ExchangeView extends LinearLayout implements AsyncExchangeRate.List
return ok;
}
int selectedNotXmrCurrency() {
return Math.max(getCurrencyA(), getCurrencyB());
}
public void doExchange() {
tvAmountB.setText("--");
// TODO cache & use cached exchange rate here
@@ -274,6 +286,7 @@ public class ExchangeView extends LinearLayout implements AsyncExchangeRate.List
public void exchange(double rate) {
if (getCurrencyA() == 0) {
if (xmrAmount == null) return;
if (!xmrAmount.isEmpty() && (rate > 0)) {
double amountB = rate * Double.parseDouble(xmrAmount);
notXmrAmount = Helper.getFormattedAmount(amountB, getCurrencyB() == 0);
@@ -282,6 +295,7 @@ public class ExchangeView extends LinearLayout implements AsyncExchangeRate.List
}
tvAmountB.setText(notXmrAmount);
} else if (getCurrencyB() == 0) {
if (notXmrAmount == null) return;
if (!notXmrAmount.isEmpty() && (rate > 0)) {
double amountB = rate * Double.parseDouble(notXmrAmount);
setXmr(Helper.getFormattedAmount(amountB, true));
@@ -339,7 +353,9 @@ public class ExchangeView extends LinearLayout implements AsyncExchangeRate.List
public void exchangeFailed() {
hideProgress();
exchange(0);
// TODO Toast it failed - I think this happens elsewhere already
if (onFailedExchangeListener != null) {
onFailedExchangeListener.onFailedExchange();
}
}
// callback from AsyncExchangeRate when we have a rate

View File

@@ -19,6 +19,7 @@
package com.m2049r.xmrwallet.layout;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
@@ -79,6 +80,12 @@ public class Toolbar extends android.support.v7.widget.Toolbar {
protected void onFinishInflate() {
super.onFinishInflate();
toolbarImage = (ImageView) findViewById(R.id.toolbarImage);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
// the vector image does not work well for androis < Nougat
toolbarImage.getLayoutParams().width = (int) getResources().getDimension(R.dimen.logo_width);
toolbarImage.setImageResource(R.drawable.logo_horizontol_xmrujo);
}
toolbarTitle = (TextView) findViewById(R.id.toolbarTitle);
toolbarSubtitle = (TextView) findViewById(R.id.toolbarSubtitle);
bDonate = (Button) findViewById(R.id.bDonate);

View File

@@ -47,10 +47,10 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
//static final int TX_PENDING = Color.rgb(72, 53, 176);
//static final int TX_FAILED = Color.rgb(208, 0, 255);
int outboundColour;
int inboundColour;
int pendingColour;
int failedColour;
private int outboundColour;
private int inboundColour;
private int pendingColour;
private int failedColour;
public interface OnInteractionListener {
void onInteraction(View view, TransactionInfo item);
@@ -59,7 +59,7 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
private final List<TransactionInfo> infoItems;
private final OnInteractionListener listener;
Context context;
private Context context;
public TransactionInfoAdapter(Context context, OnInteractionListener listener) {
this.context = context;

View File

@@ -31,9 +31,9 @@ import android.os.Process;
public class MoneroHandlerThread extends Thread {
// from src/cryptonote_config.h
static public final long THREAD_STACK_SIZE = 5 * 1024 * 1024;
int mPriority;
int mTid = -1;
Looper mLooper;
private int mPriority;
private int mTid = -1;
private Looper mLooper;
public MoneroHandlerThread(String name) {
super(null, null, name, THREAD_STACK_SIZE);
@@ -47,7 +47,7 @@ public class MoneroHandlerThread extends Thread {
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public MoneroHandlerThread(String name, int priority) {
MoneroHandlerThread(String name, int priority) {
super(null, null, name, THREAD_STACK_SIZE);
mPriority = priority;
}
@@ -57,7 +57,7 @@ public class MoneroHandlerThread extends Thread {
* setup before Looper loops.
*/
protected void onLooperPrepared() {
private void onLooperPrepared() {
}
@Override
@@ -81,7 +81,7 @@ public class MoneroHandlerThread extends Thread {
*
* @return The looper.
*/
public Looper getLooper() {
Looper getLooper() {
if (!isAlive()) {
return null;
}

View File

@@ -254,7 +254,6 @@ public class WalletService extends Service {
/////////////////////////////////////////////
/////////////////////////////////////////////
private Looper mServiceLooper;
private WalletService.ServiceHandler mServiceHandler;
private boolean errorState = false;
@@ -334,7 +333,8 @@ public class WalletService extends Service {
Wallet myWallet = getWallet();
Log.d(TAG, "SEND TX for wallet: " + myWallet.getName());
PendingTransaction pendingTransaction = myWallet.getPendingTransaction();
if (pendingTransaction.getStatus() != PendingTransaction.Status.Status_Ok) {
if ((pendingTransaction == null)
|| (pendingTransaction.getStatus() != PendingTransaction.Status.Status_Ok)) {
Log.e(TAG, "PendingTransaction is " + pendingTransaction.getStatus());
myWallet.disposePendingTransaction(); // it's broken anyway
if (observer != null) observer.onSentTransaction(false);
@@ -398,8 +398,8 @@ public class WalletService extends Service {
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new WalletService.ServiceHandler(mServiceLooper);
final Looper serviceLooper = thread.getLooper();
mServiceHandler = new WalletService.ServiceHandler(serviceLooper);
Log.d(TAG, "Service created");
}
@@ -481,7 +481,7 @@ public class WalletService extends Service {
showProgress(100);
}
showProgress(getString(R.string.status_wallet_connecting));
showProgress(-1);
showProgress(101);
// 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");

View File

@@ -24,19 +24,19 @@ import org.json.JSONException;
import org.json.JSONObject;
public class AsyncExchangeRate extends AsyncTask<String, Void, Boolean> {
static final String TAG = "AsyncGetExchangeRate";
private static final String TAG = "AsyncGetExchangeRate";
static final long TIME_REFRESH_INTERVAL = 60000; // refresh exchange rate max every minute
private static final long TIME_REFRESH_INTERVAL = 60000; // refresh exchange rate max every minute
static protected long RateTime = 0;
static protected double Rate = 0;
static protected String Fiat = null;
private static long RateTime = 0;
private static double Rate = 0;
private static String Fiat = null;
public interface Listener {
void exchange(String currencyA, String currencyB, double rate);
}
Listener listener;
private Listener listener;
public AsyncExchangeRate(Listener listener) {
super();
@@ -49,9 +49,9 @@ public class AsyncExchangeRate extends AsyncTask<String, Void, Boolean> {
super.onPreExecute();
}
boolean inverse = false;
String currencyA = null;
String currencyB = null;
private boolean inverse = false;
private String currencyA = null;
private String currencyB = null;
@Override
protected Boolean doInBackground(String... params) {
@@ -125,7 +125,7 @@ public class AsyncExchangeRate extends AsyncTask<String, Void, Boolean> {
}
}
String getExchangeRate(String fiat) {
private String getExchangeRate(String fiat) {
String jsonResponse =
Helper.getUrl("https://api.kraken.com/0/public/Ticker?pair=XMR" + fiat);
if (jsonResponse == null) return null;

View File

@@ -118,7 +118,7 @@ public class Helper {
}
/* Checks if external storage is available for read and write */
static public boolean isExternalStorageWritable() {
private static boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
return Environment.MEDIA_MOUNTED.equals(state);
}
@@ -156,7 +156,7 @@ public class Helper {
}
// amountString must have '.' as decimal point
static public String getDisplayAmount(String amountString, int maxDecimals) {
private static String getDisplayAmount(String amountString, int maxDecimals) {
int lastZero = 0;
int decimal = 0;
for (int i = amountString.length() - 1; i >= 0; i--) {

View File

@@ -22,9 +22,9 @@ import java.util.List;
public class NodeList {
static private final String TAG = "NodeList";
static public final int MAX_SIZE = 5;
private static final int MAX_SIZE = 5;
List<String> nodes = new ArrayList<>();
private List<String> nodes = new ArrayList<>();
public List<String> getNodes() {
return nodes;

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<scale
android:duration="500"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:interpolator="@android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:repeatMode="reverse"
android:toXScale="2.0"
android:toYScale="2.0" />
</set>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<padding
android:bottom="8dp"
android:left="8dp"
android:right="8dp"
android:top="8dp" />
<stroke
android:width="2dp"
android:color="@color/moneroGray" />
</shape>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -1,73 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
<item
android:drawable="@drawable/gunther_wallets_00"
android:duration="500" />
<item
android:drawable="@drawable/gunther_wallets_01"
android:duration="200" />
<item
android:drawable="@drawable/gunther_wallets_02"
android:duration="2000" />
<item
android:drawable="@drawable/gunther_wallets_03"
android:duration="1000" />
<item
android:drawable="@drawable/gunther_wallets_04"
android:duration="100" />
<item
android:drawable="@drawable/gunther_wallets_05"
android:duration="1000" />
<item
android:drawable="@drawable/gunther_wallets_06"
android:duration="0" />
<item
android:drawable="@drawable/gunther_wallets_07"
android:duration="0" />
<item
android:drawable="@drawable/gunther_wallets_08"
android:duration="0" />
<item
android:drawable="@drawable/gunther_wallets_09"
android:duration="0" />
<item
android:drawable="@drawable/gunther_wallets_10"
android:duration="0" />
<item
android:drawable="@drawable/gunther_wallets_11"
android:duration="0" />
<item
android:drawable="@drawable/gunther_wallets_12"
android:duration="0" />
<item
android:drawable="@drawable/gunther_wallets_13"
android:duration="0" />
<item
android:drawable="@drawable/gunther_wallets_14"
android:duration="0" />
<item
android:drawable="@drawable/gunther_wallets_15"
android:duration="0" />
<item
android:drawable="@drawable/gunther_wallets_16"
android:duration="0" />
<item
android:drawable="@drawable/gunther_wallets_17"
android:duration="0" />
<item
android:drawable="@drawable/gunther_wallets_18"
android:duration="5000" />
<item
android:drawable="@drawable/gunther_wallets_19"
android:duration="0" />
<item
android:drawable="@drawable/gunther_wallets_20"
android:duration="0" />
<item
android:drawable="@drawable/gunther_wallets_21"
android:duration="100" />
<item
android:drawable="@drawable/gunther_wallets_22"
android:duration="200" />
</animation-list>

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