1
mirror of https://github.com/m2049r/xmrwallet synced 2025-09-06 02:27:11 +02:00

Compare commits

..

47 Commits

Author SHA1 Message Date
m2049r
b576a9de3d bump version 2021-09-05 21:58:19 +02:00
m2049r
148faa00e4 minor fixes for confirmations indicator (#782) 2021-09-05 21:24:53 +02:00
m2049r
e82b471c14 adaptaions for monero v0.17.2.3 (#781) 2021-09-05 21:23:12 +02:00
m2049r
9ed92e5117 remove defunct strings in arabic 2021-08-07 12:31:42 +02:00
m2049r
303b3aa354 dont update on every single block (#776) 2021-08-07 11:58:54 +02:00
phwright
d801a50962 Arabic Translation (#767)
* Arabic Translation
2021-08-07 11:19:24 +02:00
m2049r
9cd8a75dc6 confirmations indicator (#775) 2021-08-07 11:17:13 +02:00
m2049r
002dfd5d58 rewrite Wallet.isSynchronized() (#774) 2021-08-07 11:04:18 +02:00
paxriel
54e54b2a8a Fixed boost JFrog repo (#772) 2021-08-04 10:37:31 +02:00
m2049r
aa768596a4 bump version 2021-05-21 08:49:13 +02:00
m2049r
c4958f6c54 use settleAmount (#768) 2021-05-21 08:45:45 +02:00
m2049r
2c2a5314d4 fix migration of devices with permission timeouts 2021-05-19 22:20:47 +02:00
m2049r
669516c60b add USE_FINGERPRINT again (#766)
and update sdk
2021-05-06 00:03:39 +02:00
m2049r
a56a29a6c4 bump version 2021-05-02 10:36:52 +02:00
m2049r
4e31f47482 new logo (#764) 2021-05-02 09:56:26 +02:00
m2049r
c1f14f9653 update gradle & deps & bump version (#760) 2021-04-25 18:24:30 +02:00
m2049r
2746c52d7b confirm checkboxes for delete confirmation dialogs (#759) 2021-04-25 14:26:46 +02:00
Baltsar
5df323bacb Update Swedish strings.xml (#754) 2021-04-23 23:32:31 +02:00
m2049r
776cc26377 refactor magic number (#756) 2021-04-23 08:57:08 +02:00
m2049r
bdfb6a90b6 update & clean build (#755) 2021-04-22 20:17:02 +02:00
m2049r
6db44dfab1 cleanup backup code (#753) 2021-04-21 19:54:20 +02:00
m2049r
c68ac7db6d bump version 2021-04-20 17:08:51 +02:00
Katant Savelev
e09862e940 Ru update for #751 (#752) 2021-04-20 17:01:01 +02:00
m2049r
c7bd7469a1 reset wallet by deleting wallet cache file (#751) 2021-04-20 13:06:14 +02:00
Katant Savelev
b39857fd2e Russian translation update (#746)
* Ru update

* Better external libs build guide

* Update BUILDING-external-libs.md
2021-04-20 12:50:48 +02:00
m2049r
8170f823ab bump version 2021-04-19 18:41:09 +02:00
m2049r
38c0ead45c random fixes (#747) 2021-04-19 17:39:12 +02:00
m2049r
1d027c1694 fix translation 2021-04-18 19:39:00 +02:00
Vlad
45dc21fbf7 Update Romanian translation (#739)
Co-authored-by: m2049r <m2049r@monerujo.io>
2021-04-18 19:19:42 +02:00
netrik182
d4f4de234a pt-br translation of new strings (#735) 2021-04-18 19:17:29 +02:00
m2049r
394d5471e3 show total incoming amount (#745) 2021-04-18 19:16:52 +02:00
m2049r
beb098adb3 build version 2021-04-18 17:38:28 +02:00
m2049r
fda370d35b bump version 2021-04-18 16:57:58 +02:00
m2049r
99681e1bbb fix permissions Q needs to read our old wallet files (#744) 2021-04-18 16:55:14 +02:00
m2049r
21f44380b1 remove persmissions 2021-04-18 13:29:16 +02:00
m2049r
f4c1af1bb8 Some refactoring (#742)
* configure enabling of shift & exchange
* refactor qr logo code
* fix rename wallet
2021-04-17 21:03:32 +02:00
m2049r
c002b81ebd use default mixin 2021-04-15 19:10:56 +02:00
m2049r
d2f07ba3b6 migrate to scoped storage and API 30 (#741) 2021-04-15 19:10:01 +02:00
m2049r
24fc27b09e show new incoming tx in subaddress details (#734) 2021-03-23 19:05:30 +01:00
m2049r
cf4ff856d5 Subaddresses as first class citizens (#733) 2021-03-22 00:31:01 +01:00
m2049r
b01de1ad6e bump version 2021-03-20 09:14:51 +01:00
anhdres
5fb1bcb552 Update strings.xml (#732)
several gender mistakes ("monedero" we're using now is masculine)
ledger translations
onboarding
sideshift integration
2021-03-19 23:38:03 +01:00
anhdres
b937ba38b6 Update help.xml (#731)
translated new text and help_ok string
2021-03-19 23:36:32 +01:00
m2049r
849718fdc7 bump version 2021-03-15 09:38:01 +01:00
m2049r
a6372f701d Merge pull request #730 from m2049r/feature_sr_transitions_s
Send & Receive UI transitions
2021-03-14 21:24:18 +01:00
m2049r
12f135bb14 refactoring 2021-03-14 21:22:49 +01:00
m2049r
16870fcbb9 Receive & send transition 2021-03-14 21:22:37 +01:00
104 changed files with 4520 additions and 2278 deletions

View File

@@ -171,6 +171,10 @@ add_library(wallet-crypto STATIC IMPORTED)
set_target_properties(wallet-crypto PROPERTIES IMPORTED_LOCATION set_target_properties(wallet-crypto PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libwallet-crypto.a) ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libwallet-crypto.a)
add_library(cryptonote_format_utils_basic STATIC IMPORTED)
set_target_properties(cryptonote_format_utils_basic PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libcryptonote_format_utils_basic.a)
############# #############
# System # System
############# #############
@@ -193,6 +197,7 @@ target_link_libraries( monerujo
wallet wallet
cryptonote_core cryptonote_core
cryptonote_basic cryptonote_basic
cryptonote_format_utils_basic
mnemonics mnemonics
ringct ringct
ringct_basic ringct_basic

View File

@@ -1,14 +1,15 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
android { android {
compileSdkVersion 29 compileSdkVersion 30
buildToolsVersion '29.0.3' buildToolsVersion '30.0.3'
ndkVersion '17.2.4988734'
defaultConfig { defaultConfig {
applicationId "com.m2049r.xmrwallet" applicationId "com.m2049r.xmrwallet"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 29 targetSdkVersion 30
versionCode 710 versionCode 1100
versionName "1.17.10 'Druk'" versionName "2.1.0 'Vertant'"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild { externalNativeBuild {
cmake { cmake {
@@ -123,14 +124,14 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0' implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation 'androidx.recyclerview:recyclerview:1.2.0'
implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'me.dm7.barcodescanner:zxing:1.9.8' implementation 'me.dm7.barcodescanner:zxing:1.9.8'
implementation "com.squareup.okhttp3:okhttp:4.9.0" implementation "com.squareup.okhttp3:okhttp:4.9.0"
implementation "com.burgstaller:okhttp-digest:2.1" implementation "io.github.rburgst:okhttp-digest:2.5"
implementation "com.jakewharton.timber:timber:4.7.1" implementation "com.jakewharton.timber:timber:4.7.1"
implementation 'com.nulab-inc:zxcvbn:1.3.0' implementation 'com.nulab-inc:zxcvbn:1.3.0'

View File

@@ -4,20 +4,19 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" /> <uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.NFC" /> <uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application <application
android:requestLegacyExternalStorage="true"
android:name=".XmrWalletApplication" android:name=".XmrWalletApplication"
android:allowBackup="false" android:allowBackup="false"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:preserveLegacyExternalStorage="true"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/MyMaterialTheme" android:theme="@style/MyMaterialTheme"
android:usesCleartextTraffic="true"> android:usesCleartextTraffic="true">
@@ -36,7 +35,7 @@
android:configChanges="orientation|keyboardHidden|uiMode" android:configChanges="orientation|keyboardHidden|uiMode"
android:label="@string/wallet_activity_name" android:label="@string/wallet_activity_name"
android:launchMode="singleTask" android:launchMode="singleTask"
android:screenOrientation="behind"/> android:screenOrientation="behind" />
<activity <activity
android:name=".LoginActivity" android:name=".LoginActivity"
android:configChanges="orientation|keyboardHidden|uiMode" android:configChanges="orientation|keyboardHidden|uiMode"

View File

@@ -914,7 +914,7 @@ Java_com_m2049r_xmrwallet_model_Wallet_refreshAsync(JNIEnv *env, jobject instanc
//virtual void rescanBlockchainAsync() = 0; //virtual void rescanBlockchainAsync() = 0;
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_rescanBlockchainAsync(JNIEnv *env, jobject instance) { Java_com_m2049r_xmrwallet_model_Wallet_rescanBlockchainAsyncJ(JNIEnv *env, jobject instance) {
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance); Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
wallet->rescanBlockchainAsync(); wallet->rescanBlockchainAsync();
} }

View File

@@ -31,10 +31,11 @@ import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.PowerManager; import android.os.PowerManager;
import android.widget.Toast;
import androidx.annotation.CallSuper; import androidx.annotation.CallSuper;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import android.widget.Toast;
import com.m2049r.xmrwallet.data.BarcodeData; import com.m2049r.xmrwallet.data.BarcodeData;
import com.m2049r.xmrwallet.dialog.ProgressDialog; import com.m2049r.xmrwallet.dialog.ProgressDialog;
@@ -46,7 +47,8 @@ import java.io.IOException;
import timber.log.Timber; import timber.log.Timber;
public class BaseActivity extends SecureActivity implements GenerateReviewFragment.ProgressListener { public class BaseActivity extends SecureActivity
implements GenerateReviewFragment.ProgressListener, SubaddressFragment.ProgressListener {
ProgressDialog progressDialog = null; ProgressDialog progressDialog = null;

View File

@@ -307,7 +307,6 @@ public class GenerateReviewFragment extends Fragment {
void dismissProgressDialog(); void dismissProgressDialog();
} }
public interface AcceptListener { public interface AcceptListener {
void onAccept(String name, String password); void onAccept(String name, String password);
} }

File diff suppressed because it is too large Load Diff

View File

@@ -31,7 +31,6 @@ import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@@ -62,8 +61,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
private WalletInfoAdapter adapter; private WalletInfoAdapter adapter;
private List<WalletManager.WalletInfo> walletList = new ArrayList<>(); private final List<WalletManager.WalletInfo> walletList = new ArrayList<>();
private List<WalletManager.WalletInfo> displayedList = new ArrayList<>();
private View tvGuntherSays; private View tvGuntherSays;
private ImageView ivGunther; private ImageView ivGunther;
@@ -82,13 +80,15 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
void onWalletDetails(String wallet); void onWalletDetails(String wallet);
void onWalletReceive(String wallet);
void onWalletRename(String name); void onWalletRename(String name);
void onWalletBackup(String name); void onWalletBackup(String name);
void onWalletArchive(String walletName); void onWalletRestore();
void onWalletDelete(String walletName);
void onWalletDeleteCache(String walletName);
void onAddWallet(String type); void onAddWallet(String type);
@@ -112,7 +112,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
} }
@Override @Override
public void onAttach(Context context) { public void onAttach(@NonNull Context context) {
super.onAttach(context); super.onAttach(context);
if (context instanceof Listener) { if (context instanceof Listener) {
this.activityCallback = (Listener) context; this.activityCallback = (Listener) context;
@@ -202,12 +202,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
// Wallet touched // Wallet touched
@Override @Override
public void onInteraction(final View view, final WalletManager.WalletInfo infoItem) { public void onInteraction(final View view, final WalletManager.WalletInfo infoItem) {
String addressPrefix = WalletManager.getInstance().addressPrefix(); openWallet(infoItem.getName(), false);
if (addressPrefix.indexOf(infoItem.address.charAt(0)) < 0) {
Toast.makeText(getActivity(), getString(R.string.prompt_wrong_net), Toast.LENGTH_LONG).show();
return;
}
openWallet(infoItem.name, false);
} }
private void openWallet(String name, boolean streetmode) { private void openWallet(String name, boolean streetmode) {
@@ -216,51 +211,34 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
@Override @Override
public boolean onContextInteraction(MenuItem item, WalletManager.WalletInfo listItem) { public boolean onContextInteraction(MenuItem item, WalletManager.WalletInfo listItem) {
switch (item.getItemId()) { final int id = item.getItemId();
case R.id.action_streetmode: if (id == R.id.action_streetmode) {
openWallet(listItem.name, true); openWallet(listItem.getName(), true);
break; } else if (id == R.id.action_info) {
case R.id.action_info: showInfo(listItem.getName());
showInfo(listItem.name); } else if (id == R.id.action_rename) {
break; activityCallback.onWalletRename(listItem.getName());
case R.id.action_receive: } else if (id == R.id.action_backup) {
showReceive(listItem.name); activityCallback.onWalletBackup(listItem.getName());
break; } else if (id == R.id.action_archive) {
case R.id.action_rename: activityCallback.onWalletDelete(listItem.getName());
activityCallback.onWalletRename(listItem.name); } else if (id == R.id.action_deletecache) {
break; activityCallback.onWalletDeleteCache(listItem.getName());
case R.id.action_backup: } else {
activityCallback.onWalletBackup(listItem.name); return super.onContextItemSelected(item);
break;
case R.id.action_archive:
activityCallback.onWalletArchive(listItem.name);
break;
default:
return super.onContextItemSelected(item);
} }
return true; return true;
} }
private void filterList() {
displayedList.clear();
String addressPrefix = WalletManager.getInstance().addressPrefix();
for (WalletManager.WalletInfo s : walletList) {
if (addressPrefix.indexOf(s.address.charAt(0)) >= 0) displayedList.add(s);
}
}
public void loadList() { public void loadList() {
Timber.d("loadList()"); Timber.d("loadList()");
WalletManager mgr = WalletManager.getInstance(); WalletManager mgr = WalletManager.getInstance();
List<WalletManager.WalletInfo> walletInfos =
mgr.findWallets(activityCallback.getStorageRoot());
walletList.clear(); walletList.clear();
walletList.addAll(walletInfos); walletList.addAll(mgr.findWallets(activityCallback.getStorageRoot()));
filterList(); adapter.setInfos(walletList);
adapter.setInfos(displayedList);
// deal with Gunther & FAB animation // deal with Gunther & FAB animation
if (displayedList.isEmpty()) { if (walletList.isEmpty()) {
fab.startAnimation(fab_pulse); fab.startAnimation(fab_pulse);
if (ivGunther.getDrawable() == null) { if (ivGunther.getDrawable() == null) {
ivGunther.setImageResource(R.drawable.ic_emptygunther); ivGunther.setImageResource(R.drawable.ic_emptygunther);
@@ -279,7 +257,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
.getSharedPreferences(KeyStoreHelper.SecurityConstants.WALLET_PASS_PREFS_NAME, Context.MODE_PRIVATE) .getSharedPreferences(KeyStoreHelper.SecurityConstants.WALLET_PASS_PREFS_NAME, Context.MODE_PRIVATE)
.getAll().keySet(); .getAll().keySet();
for (WalletManager.WalletInfo s : walletList) { for (WalletManager.WalletInfo s : walletList) {
removedWallets.remove(s.name); removedWallets.remove(s.getName());
} }
for (String name : removedWallets) { for (String name : removedWallets) {
KeyStoreHelper.removeWalletUserPass(getActivity(), name); KeyStoreHelper.removeWalletUserPass(getActivity(), name);
@@ -290,10 +268,6 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
activityCallback.onWalletDetails(name); activityCallback.onWalletDetails(name);
} }
private void showReceive(@NonNull String name) {
activityCallback.onWalletReceive(name);
}
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@@ -301,7 +275,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
} }
@Override @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.list_menu, menu); inflater.inflate(R.menu.list_menu, menu);
super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater);
} }
@@ -371,37 +345,29 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
@Override @Override
public void onClick(View v) { public void onClick(View v) {
int id = v.getId(); final int id = v.getId();
Timber.d("onClick %d/%d", id, R.id.fabLedger); Timber.d("onClick %d/%d", id, R.id.fabLedger);
switch (id) { if (id == R.id.fab) {
case R.id.fab: animateFAB();
animateFAB(); } else if (id == R.id.fabNew) {
break; fabScreen.setVisibility(View.INVISIBLE);
case R.id.fabNew: isFabOpen = false;
fabScreen.setVisibility(View.INVISIBLE); activityCallback.onAddWallet(GenerateFragment.TYPE_NEW);
isFabOpen = false; } else if (id == R.id.fabView) {
activityCallback.onAddWallet(GenerateFragment.TYPE_NEW); animateFAB();
break; activityCallback.onAddWallet(GenerateFragment.TYPE_VIEWONLY);
case R.id.fabView: } else if (id == R.id.fabKey) {
animateFAB(); animateFAB();
activityCallback.onAddWallet(GenerateFragment.TYPE_VIEWONLY); activityCallback.onAddWallet(GenerateFragment.TYPE_KEY);
break; } else if (id == R.id.fabSeed) {
case R.id.fabKey: animateFAB();
animateFAB(); activityCallback.onAddWallet(GenerateFragment.TYPE_SEED);
activityCallback.onAddWallet(GenerateFragment.TYPE_KEY); } else if (id == R.id.fabLedger) {
break; Timber.d("FAB_LEDGER");
case R.id.fabSeed: animateFAB();
animateFAB(); activityCallback.onAddWallet(GenerateFragment.TYPE_LEDGER);
activityCallback.onAddWallet(GenerateFragment.TYPE_SEED); } else if (id == R.id.fabScreen) {
break; animateFAB();
case R.id.fabLedger:
Timber.d("FAB_LEDGER");
animateFAB();
activityCallback.onAddWallet(GenerateFragment.TYPE_LEDGER);
break;
case R.id.fabScreen:
animateFAB();
break;
} }
} }

View File

@@ -228,24 +228,23 @@ public class NodeFragment extends Fragment
// open up edit dialog // open up edit dialog
@Override @Override
public void onLongInteraction(final View view, final NodeInfo nodeItem) { public boolean onLongInteraction(final View view, final NodeInfo nodeItem) {
Timber.d("onLongInteraction"); Timber.d("onLongInteraction");
EditDialog diag = createEditDialog(nodeItem); EditDialog diag = createEditDialog(nodeItem);
if (diag != null) { if (diag != null) {
diag.show(); diag.show();
} }
return true;
} }
@Override @Override
public void onClick(View v) { public void onClick(View v) {
int id = v.getId(); int id = v.getId();
switch (id) { if (id == R.id.fab) {
case R.id.fab: EditDialog diag = createEditDialog(null);
EditDialog diag = createEditDialog(null); if (diag != null) {
if (diag != null) { diag.show();
diag.show(); }
}
break;
} }
} }

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2021 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 com.m2049r.xmrwallet.model.Wallet;
public interface OnBlockUpdateListener {
void onBlockUpdate(final Wallet wallet);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,251 @@
/*
* Copyright (c) 2017 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.transition.MaterialElevationScale;
import com.m2049r.xmrwallet.data.Subaddress;
import com.m2049r.xmrwallet.layout.SubaddressInfoAdapter;
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
import com.m2049r.xmrwallet.model.TransactionInfo;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor;
import com.m2049r.xmrwallet.widget.Toolbar;
import java.util.ArrayList;
import java.util.List;
import lombok.RequiredArgsConstructor;
import timber.log.Timber;
public class SubaddressFragment extends Fragment implements SubaddressInfoAdapter.OnInteractionListener,
View.OnClickListener, OnBlockUpdateListener {
static public final String KEY_MODE = "mode";
static public final String MODE_MANAGER = "manager";
private SubaddressInfoAdapter adapter;
private Listener activityCallback;
private Wallet wallet;
// Container Activity must implement this interface
public interface Listener {
void onSubaddressSelected(Subaddress subaddress);
void setSubtitle(String title);
void setToolbarButton(int type);
void showSubaddress(View view, final int subaddressIndex);
}
public interface ProgressListener {
void showProgressDialog(int msgId);
void showLedgerProgressDialog(int mode);
void dismissProgressDialog();
}
private ProgressListener progressCallback = null;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof ProgressListener) {
progressCallback = (ProgressListener) context;
}
if (context instanceof Listener) {
activityCallback = (Listener) context;
} else {
throw new ClassCastException(context.toString()
+ " must implement Listener");
}
}
@Override
public void onPause() {
Timber.d("onPause()");
super.onPause();
}
@Override
public void onResume() {
super.onResume();
activityCallback.setSubtitle(getString(R.string.subbaddress_title));
activityCallback.setToolbarButton(Toolbar.BUTTON_BACK);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Timber.d("onCreateView");
final Bundle b = getArguments();
managerMode = ((b != null) && (MODE_MANAGER.equals(b.getString(KEY_MODE))));
View view = inflater.inflate(R.layout.fragment_subaddress, container, false);
final MaterialElevationScale exitTransition = new MaterialElevationScale(false);
exitTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
setExitTransition(exitTransition);
final MaterialElevationScale reenterTransition = new MaterialElevationScale(true);
reenterTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
setReenterTransition(reenterTransition);
view.findViewById(R.id.fab).setOnClickListener(this);
if (managerMode) {
view.findViewById(R.id.tvInstruction).setVisibility(View.GONE);
view.findViewById(R.id.tvHint).setVisibility(View.GONE);
}
final RecyclerView list = view.findViewById(R.id.list);
adapter = new SubaddressInfoAdapter(getActivity(), this);
list.setAdapter(adapter);
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
list.scrollToPosition(positionStart);
}
});
Helper.hideKeyboard(getActivity());
wallet = WalletManager.getInstance().getWallet();
loadList();
return view;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
postponeEnterTransition();
view.getViewTreeObserver().addOnPreDrawListener(() -> {
startPostponedEnterTransition();
return true;
});
}
public void loadList() {
Timber.d("loadList()");
final int numSubaddresses = wallet.getNumSubaddresses();
final List<Subaddress> list = new ArrayList<>();
for (int i = 0; i < numSubaddresses; i++) {
list.add(wallet.getSubaddressObject(i));
}
adapter.setInfos(list);
}
@Override
public void onBlockUpdate(Wallet wallet) {
loadList();
}
@Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.fab) {
getNewSubaddress();
}
}
private int lastUsedSubaddress() {
int lastUsedSubaddress = 0;
for (TransactionInfo info : wallet.getHistory().getAll()) {
if (info.addressIndex > lastUsedSubaddress)
lastUsedSubaddress = info.addressIndex;
}
return lastUsedSubaddress;
}
private void getNewSubaddress() {
final int maxSubaddresses = lastUsedSubaddress() + wallet.getDeviceType().getSubaddressLookahead();
if (wallet.getNumSubaddresses() < maxSubaddresses)
new AsyncSubaddress().executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR);
else
Toast.makeText(getActivity(), getString(R.string.max_subaddress_warning), Toast.LENGTH_LONG).show();
}
@SuppressLint("StaticFieldLeak")
@RequiredArgsConstructor
private class AsyncSubaddress extends AsyncTask<Void, Void, Boolean> {
boolean dialogOpened = false;
@Override
protected void onPreExecute() {
super.onPreExecute();
if ((wallet.getDeviceType() == Wallet.Device.Device_Ledger) && (progressCallback != null)) {
progressCallback.showLedgerProgressDialog(LedgerProgressDialog.TYPE_SUBADDRESS);
dialogOpened = true;
}
}
@Override
protected Boolean doInBackground(Void... params) {
if (params.length != 0) return false;
wallet.getNewSubaddress();
wallet.store();
return true;
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (dialogOpened)
progressCallback.dismissProgressDialog();
if (!isAdded()) // never mind then
return;
loadList();
}
}
boolean managerMode = false;
// Callbacks from SubaddressInfoAdapter
@Override
public void onInteraction(final View view, final Subaddress subaddress) {
if (managerMode)
activityCallback.showSubaddress(view, subaddress.getAddressIndex());
else
activityCallback.onSubaddressSelected(subaddress); // also closes the fragment with onBackpressed()
}
@Override
public boolean onLongInteraction(View view, Subaddress subaddress) {
activityCallback.showSubaddress(view, subaddress.getAddressIndex());
return false;
}
}

View File

@@ -0,0 +1,175 @@
/*
* Copyright (c) 2017 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet;
import android.content.Context;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.textfield.TextInputLayout;
import com.google.android.material.transition.MaterialContainerTransform;
import com.m2049r.xmrwallet.data.Subaddress;
import com.m2049r.xmrwallet.layout.TransactionInfoAdapter;
import com.m2049r.xmrwallet.model.TransactionInfo;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.ThemeHelper;
import com.m2049r.xmrwallet.widget.Toolbar;
import java.util.ArrayList;
import java.util.List;
import timber.log.Timber;
public class SubaddressInfoFragment extends Fragment
implements TransactionInfoAdapter.OnInteractionListener, OnBlockUpdateListener {
private TransactionInfoAdapter adapter;
private Subaddress subaddress;
private TextInputLayout etName;
private TextView tvAddress;
private TextView tvTxLabel;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_subaddressinfo, container, false);
etName = view.findViewById(R.id.etName);
tvAddress = view.findViewById(R.id.tvAddress);
tvTxLabel = view.findViewById(R.id.tvTxLabel);
final RecyclerView list = view.findViewById(R.id.list);
adapter = new TransactionInfoAdapter(getActivity(), this);
list.setAdapter(adapter);
final Wallet wallet = activityCallback.getWallet();
Bundle b = getArguments();
final int subaddressIndex = b.getInt("subaddressIndex");
subaddress = wallet.getSubaddressObject(subaddressIndex);
etName.getEditText().setText(subaddress.getDisplayLabel());
tvAddress.setText(getContext().getString(R.string.subbaddress_info_subtitle,
subaddress.getAddressIndex(), subaddress.getSquashedAddress()));
etName.getEditText().setOnFocusChangeListener((v, hasFocus) -> {
if (!hasFocus) {
wallet.setSubaddressLabel(subaddressIndex, etName.getEditText().getText().toString());
}
});
etName.getEditText().setOnEditorActionListener((v, actionId, event) -> {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN))
|| (actionId == EditorInfo.IME_ACTION_DONE)) {
Helper.hideKeyboard(getActivity());
wallet.setSubaddressLabel(subaddressIndex, etName.getEditText().getText().toString());
onRefreshed(wallet);
return true;
}
return false;
});
onRefreshed(wallet);
return view;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final MaterialContainerTransform transform = new MaterialContainerTransform();
transform.setDrawingViewId(R.id.fragment_container);
transform.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
transform.setAllContainerColors(ThemeHelper.getThemedColor(getContext(), android.R.attr.colorBackground));
setSharedElementEnterTransition(transform);
}
public void onRefreshed(final Wallet wallet) {
Timber.d("onRefreshed");
List<TransactionInfo> list = new ArrayList<>();
for (TransactionInfo info : wallet.getHistory().getAll()) {
if (info.addressIndex == subaddress.getAddressIndex())
list.add(info);
}
adapter.setInfos(list);
if (list.isEmpty())
tvTxLabel.setText(R.string.subaddress_notx_label);
else
tvTxLabel.setText(R.string.subaddress_tx_label);
}
@Override
public void onBlockUpdate(Wallet wallet) {
onRefreshed(wallet);
}
// Callbacks from TransactionInfoAdapter
@Override
public void onInteraction(final View view, final TransactionInfo infoItem) {
activityCallback.onTxDetailsRequest(view, infoItem);
}
Listener activityCallback;
// Container Activity must implement this interface
public interface Listener {
void onTxDetailsRequest(View view, TransactionInfo info);
Wallet getWallet();
void setToolbarButton(int type);
void setTitle(String title, String subtitle);
void setSubtitle(String subtitle);
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof Listener) {
this.activityCallback = (Listener) context;
} else {
throw new ClassCastException(context.toString()
+ " must implement Listener");
}
}
@Override
public void onResume() {
super.onResume();
Timber.d("onResume()");
activityCallback.setSubtitle(getString(R.string.subbaddress_title));
activityCallback.setToolbarButton(Toolbar.BUTTON_BACK);
}
@Override
public void onPause() {
super.onPause();
}
}

View File

@@ -22,7 +22,9 @@ import android.content.Intent;
import android.graphics.Paint; import android.graphics.Paint;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.text.Html;
import android.text.InputType; import android.text.InputType;
import android.text.Spanned;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
@@ -37,12 +39,14 @@ import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.google.android.material.transition.MaterialContainerTransform; import com.google.android.material.transition.MaterialContainerTransform;
import com.google.android.material.transition.MaterialElevationScale;
import com.m2049r.xmrwallet.data.Subaddress;
import com.m2049r.xmrwallet.data.UserNotes; import com.m2049r.xmrwallet.data.UserNotes;
import com.m2049r.xmrwallet.model.TransactionInfo; import com.m2049r.xmrwallet.model.TransactionInfo;
import com.m2049r.xmrwallet.model.Transfer; import com.m2049r.xmrwallet.model.Transfer;
import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.util.ThemeHelper;
import com.m2049r.xmrwallet.util.Helper; import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.ThemeHelper;
import com.m2049r.xmrwallet.widget.Toolbar; import com.m2049r.xmrwallet.widget.Toolbar;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@@ -52,6 +56,8 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.TimeZone; import java.util.TimeZone;
import timber.log.Timber;
public class TxFragment extends Fragment { public class TxFragment extends Fragment {
static public final String ARG_INFO = "info"; static public final String ARG_INFO = "info";
@@ -88,11 +94,16 @@ public class TxFragment extends Fragment {
private ImageView tvXmrToLogo; private ImageView tvXmrToLogo;
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_tx_info, container, false); View view = inflater.inflate(R.layout.fragment_tx_info, container, false);
final MaterialElevationScale exitTransition = new MaterialElevationScale(false);
exitTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
setExitTransition(exitTransition);
final MaterialElevationScale reenterTransition = new MaterialElevationScale(true);
reenterTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
setReenterTransition(reenterTransition);
cvXmrTo = view.findViewById(R.id.cvXmrTo); cvXmrTo = view.findViewById(R.id.cvXmrTo);
tvTxXmrToKey = view.findViewById(R.id.tvTxXmrToKey); tvTxXmrToKey = view.findViewById(R.id.tvTxXmrToKey);
tvDestinationBtc = view.findViewById(R.id.tvDestinationBtc); tvDestinationBtc = view.findViewById(R.id.tvDestinationBtc);
@@ -121,9 +132,8 @@ public class TxFragment extends Fragment {
Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show(); Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show();
}); });
Bundle args = getArguments(); info = getArguments().getParcelable(ARG_INFO);
TransactionInfo info = args.getParcelable(ARG_INFO); show();
show(info);
return view; return view;
} }
@@ -192,7 +202,7 @@ public class TxFragment extends Fragment {
TransactionInfo info = null; TransactionInfo info = null;
UserNotes userNotes = null; UserNotes userNotes = null;
void loadNotes(TransactionInfo info) { void loadNotes() {
if ((userNotes == null) || (info.notes == null)) { if ((userNotes == null) || (info.notes == null)) {
info.notes = activityCallback.getTxNotes(info.hash); info.notes = activityCallback.getTxNotes(info.hash);
} }
@@ -205,19 +215,28 @@ public class TxFragment extends Fragment {
tvTxFee.setTextColor(clr); tvTxFee.setTextColor(clr);
} }
private void show(TransactionInfo info) { private void showSubaddressLabel() {
final Subaddress subaddress = activityCallback.getWalletSubaddress(info.accountIndex, info.addressIndex);
final Context ctx = getContext();
Spanned label = Html.fromHtml(ctx.getString(R.string.tx_account_formatted,
info.accountIndex, info.addressIndex,
Integer.toHexString(ContextCompat.getColor(ctx, R.color.monerujoGreen) & 0xFFFFFF),
Integer.toHexString(ContextCompat.getColor(ctx, R.color.monerujoBackground) & 0xFFFFFF),
subaddress.getDisplayLabel()));
tvAccount.setText(label);
tvAccount.setOnClickListener(v -> activityCallback.showSubaddress(v, info.addressIndex));
}
private void show() {
if (info.txKey == null) { if (info.txKey == null) {
info.txKey = activityCallback.getTxKey(info.hash); info.txKey = activityCallback.getTxKey(info.hash);
} }
if (info.address == null) { if (info.address == null) {
info.address = activityCallback.getTxAddress(info.account, info.subaddress); info.address = activityCallback.getTxAddress(info.accountIndex, info.addressIndex);
} }
loadNotes(info); loadNotes();
activityCallback.setSubtitle(getString(R.string.tx_title)); showSubaddressLabel();
activityCallback.setToolbarButton(Toolbar.BUTTON_BACK);
tvAccount.setText(getString(R.string.tx_account_formatted, info.account, info.subaddress));
tvAddress.setText(info.address); tvAddress.setText(info.address);
tvTxTimestamp.setText(TS_FORMATTER.format(new Date(info.timestamp * 1000))); tvTxTimestamp.setText(TS_FORMATTER.format(new Date(info.timestamp * 1000)));
@@ -256,8 +275,8 @@ public class TxFragment extends Fragment {
setTxColour(ContextCompat.getColor(getContext(), R.color.tx_minus)); setTxColour(ContextCompat.getColor(getContext(), R.color.tx_minus));
} }
Set<String> destinations = new HashSet<>(); Set<String> destinations = new HashSet<>();
StringBuffer sb = new StringBuffer(); StringBuilder sb = new StringBuilder();
StringBuffer dstSb = new StringBuffer(); StringBuilder dstSb = new StringBuilder();
if (info.transfers != null) { if (info.transfers != null) {
boolean newline = false; boolean newline = false;
for (Transfer transfer : info.transfers) { for (Transfer transfer : info.transfers) {
@@ -281,14 +300,12 @@ public class TxFragment extends Fragment {
} }
} else { } else {
sb.append("-"); sb.append("-");
dstSb.append(info.direction == dstSb.append(info.direction == TransactionInfo.Direction.Direction_In ?
TransactionInfo.Direction.Direction_In ? activityCallback.getWalletSubaddress(info.accountIndex, info.addressIndex).getAddress() :
activityCallback.getWalletSubaddress(info.account, info.subaddress) :
"-"); "-");
} }
tvTxTransfers.setText(sb.toString()); tvTxTransfers.setText(sb.toString());
tvDestination.setText(dstSb.toString()); tvDestination.setText(dstSb.toString());
this.info = info;
showBtcInfo(); showBtcInfo();
} }
@@ -334,7 +351,7 @@ public class TxFragment extends Fragment {
final MaterialContainerTransform transform = new MaterialContainerTransform(); final MaterialContainerTransform transform = new MaterialContainerTransform();
transform.setDrawingViewId(R.id.fragment_container); transform.setDrawingViewId(R.id.fragment_container);
transform.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration)); transform.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
transform.setAllContainerColors(ThemeHelper.getThemedColor(getContext(), R.attr.colorSurface)); transform.setAllContainerColors(ThemeHelper.getThemedColor(getContext(), android.R.attr.colorBackground));
setSharedElementEnterTransition(transform); setSharedElementEnterTransition(transform);
} }
@@ -347,7 +364,7 @@ public class TxFragment extends Fragment {
Listener activityCallback; Listener activityCallback;
public interface Listener { public interface Listener {
String getWalletSubaddress(int accountIndex, int subaddressIndex); Subaddress getWalletSubaddress(int accountIndex, int subaddressIndex);
String getTxKey(String hash); String getTxKey(String hash);
@@ -361,6 +378,8 @@ public class TxFragment extends Fragment {
void setSubtitle(String subtitle); void setSubtitle(String subtitle);
void showSubaddress(View view, final int subaddressIndex);
} }
@Override @Override
@@ -385,4 +404,13 @@ public class TxFragment extends Fragment {
Helper.hideKeyboard(getActivity()); Helper.hideKeyboard(getActivity());
super.onPause(); super.onPause();
} }
@Override
public void onResume() {
super.onResume();
Timber.d("onResume()");
activityCallback.setSubtitle(getString(R.string.tx_title));
activityCallback.setToolbarButton(Toolbar.BUTTON_BACK);
showSubaddressLabel();
}
} }

File diff suppressed because it is too large Load Diff

View File

@@ -36,6 +36,7 @@ import android.widget.ProgressBar;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
@@ -63,7 +64,7 @@ import timber.log.Timber;
public class WalletFragment extends Fragment public class WalletFragment extends Fragment
implements TransactionInfoAdapter.OnInteractionListener { implements TransactionInfoAdapter.OnInteractionListener {
private TransactionInfoAdapter adapter; private TransactionInfoAdapter adapter;
private NumberFormat formatter = NumberFormat.getInstance(); private final NumberFormat formatter = NumberFormat.getInstance();
private TextView tvStreetView; private TextView tvStreetView;
private LinearLayout llBalance; private LinearLayout llBalance;
@@ -77,10 +78,11 @@ public class WalletFragment extends Fragment
private Button bSend; private Button bSend;
private ImageView ivStreetGunther; private ImageView ivStreetGunther;
private Drawable streetGunther = null; private Drawable streetGunther = null;
RecyclerView txlist;
private Spinner sCurrency; private Spinner sCurrency;
private List<String> dismissedTransactions = new ArrayList<>(); private final List<String> dismissedTransactions = new ArrayList<>();
public void resetDismissedTransactions() { public void resetDismissedTransactions() {
dismissedTransactions.clear(); dismissedTransactions.clear();
@@ -93,7 +95,7 @@ public class WalletFragment extends Fragment
} }
@Override @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
if (activityCallback.hasWallet()) if (activityCallback.hasWallet())
inflater.inflate(R.menu.wallet_menu, menu); inflater.inflate(R.menu.wallet_menu, menu);
super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater);
@@ -123,21 +125,28 @@ public class WalletFragment extends Fragment
sCurrency = view.findViewById(R.id.sCurrency); sCurrency = view.findViewById(R.id.sCurrency);
List<String> currencies = new ArrayList<>(); List<String> currencies = new ArrayList<>();
currencies.add(Helper.BASE_CRYPTO); currencies.add(Helper.BASE_CRYPTO);
currencies.addAll(Arrays.asList(getResources().getStringArray(R.array.currency))); if (Helper.SHOW_EXCHANGERATES)
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(getContext(), R.layout.item_spinner_balance, currencies); currencies.addAll(Arrays.asList(getResources().getStringArray(R.array.currency)));
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(requireContext(), R.layout.item_spinner_balance, currencies);
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
sCurrency.setAdapter(spinnerAdapter); sCurrency.setAdapter(spinnerAdapter);
bSend = view.findViewById(R.id.bSend); bSend = view.findViewById(R.id.bSend);
bReceive = view.findViewById(R.id.bReceive); bReceive = view.findViewById(R.id.bReceive);
RecyclerView recyclerView = view.findViewById(R.id.list); txlist = view.findViewById(R.id.list);
adapter = new TransactionInfoAdapter(getActivity(), this); adapter = new TransactionInfoAdapter(getActivity(), this);
recyclerView.setAdapter(adapter); txlist.setAdapter(adapter);
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
if ((positionStart == 0) && (txlist.computeVerticalScrollOffset() == 0))
txlist.scrollToPosition(positionStart);
}
});
SwipeableRecyclerViewTouchListener swipeTouchListener = txlist.addOnItemTouchListener(
new SwipeableRecyclerViewTouchListener(recyclerView, new SwipeableRecyclerViewTouchListener(txlist,
new SwipeableRecyclerViewTouchListener.SwipeListener() { new SwipeableRecyclerViewTouchListener.SwipeListener() {
@Override @Override
public boolean canSwipeLeft(int position) { public boolean canSwipeLeft(int position) {
@@ -164,23 +173,10 @@ public class WalletFragment extends Fragment
adapter.removeItem(position); adapter.removeItem(position);
} }
} }
}); }));
recyclerView.addOnItemTouchListener(swipeTouchListener); bSend.setOnClickListener(v -> activityCallback.onSendRequest(v));
bReceive.setOnClickListener(v -> activityCallback.onWalletReceive(v));
bSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
activityCallback.onSendRequest();
}
});
bReceive.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
activityCallback.onWalletReceive();
}
});
sCurrency.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { sCurrency.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override @Override
@@ -203,6 +199,16 @@ public class WalletFragment extends Fragment
return view; return view;
} }
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
postponeEnterTransition();
view.getViewTreeObserver().addOnPreDrawListener(() -> {
startPostponedEnterTransition();
return true;
});
}
void showBalance(String balance) { void showBalance(String balance) {
tvBalance.setText(balance); tvBalance.setText(balance);
final boolean streetMode = activityCallback.isStreetMode(); final boolean streetMode = activityCallback.isStreetMode();
@@ -260,24 +266,14 @@ public class WalletFragment extends Fragment
@Override @Override
public void onSuccess(final ExchangeRate exchangeRate) { public void onSuccess(final ExchangeRate exchangeRate) {
if (isAdded()) if (isAdded())
new Handler(Looper.getMainLooper()).post(new Runnable() { new Handler(Looper.getMainLooper()).post(() -> exchange(exchangeRate));
@Override
public void run() {
exchange(exchangeRate);
}
});
} }
@Override @Override
public void onError(final Exception e) { public void onError(final Exception e) {
Timber.e(e.getLocalizedMessage()); Timber.e(e.getLocalizedMessage());
if (isAdded()) if (isAdded())
new Handler(Looper.getMainLooper()).post(new Runnable() { new Handler(Looper.getMainLooper()).post(() -> exchangeFailed());
@Override
public void run() {
exchangeFailed();
}
});
} }
}); });
} else { } else {
@@ -339,24 +335,39 @@ public class WalletFragment extends Fragment
final MaterialElevationScale reenterTransition = new MaterialElevationScale(true); final MaterialElevationScale reenterTransition = new MaterialElevationScale(true);
reenterTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration)); reenterTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
setReenterTransition(reenterTransition); setReenterTransition(reenterTransition);
activityCallback.onTxDetailsRequest(view, infoItem); activityCallback.onTxDetailsRequest(view, infoItem);
} }
// called from activity // called from activity
public void onRefreshed(final Wallet wallet, final boolean full) {
// if account index has changed scroll to top?
private int accountIndex = 0;
public void onRefreshed(final Wallet wallet, boolean full) {
Timber.d("onRefreshed(%b)", full); Timber.d("onRefreshed(%b)", full);
if (adapter.needsTransactionUpdateOnNewBlock()) {
wallet.getHistory().refresh();
full = true;
}
if (full) { if (full) {
List<TransactionInfo> list = new ArrayList<>(); List<TransactionInfo> list = new ArrayList<>();
final long streetHeight = activityCallback.getStreetModeHeight(); final long streetHeight = activityCallback.getStreetModeHeight();
Timber.d("StreetHeight=%d", streetHeight); Timber.d("StreetHeight=%d", streetHeight);
wallet.getHistory().refresh();
for (TransactionInfo info : wallet.getHistory().getAll()) { for (TransactionInfo info : wallet.getHistory().getAll()) {
Timber.d("TxHeight=%d", info.blockheight); Timber.d("TxHeight=%d, Label=%s", info.blockheight, info.subaddressLabel);
if ((info.isPending || (info.blockheight >= streetHeight)) if ((info.isPending || (info.blockheight >= streetHeight))
&& !dismissedTransactions.contains(info.hash)) && !dismissedTransactions.contains(info.hash))
list.add(info); list.add(info);
} }
adapter.setInfos(list); adapter.setInfos(list);
if (accountIndex != wallet.getAccountIndex()) {
accountIndex = wallet.getAccountIndex();
txlist.scrollToPosition(0);
}
} }
updateStatus(wallet); updateStatus(wallet);
} }
@@ -418,7 +429,6 @@ public class WalletFragment extends Fragment
void setActivityTitle(Wallet wallet) { void setActivityTitle(Wallet wallet) {
if (wallet == null) return; if (wallet == null) return;
walletTitle = wallet.getName(); walletTitle = wallet.getName();
String watchOnly = (wallet.isWatchOnly() ? getString(R.string.label_watchonly) : "");
walletSubtitle = wallet.getAccountLabel(); walletSubtitle = wallet.getAccountLabel();
activityCallback.setTitle(walletTitle, walletSubtitle); activityCallback.setTitle(walletTitle, walletSubtitle);
Timber.d("wallet title is %s", walletTitle); Timber.d("wallet title is %s", walletTitle);
@@ -442,7 +452,7 @@ public class WalletFragment extends Fragment
balance = wallet.getBalance(); balance = wallet.getBalance();
unlockedBalance = wallet.getUnlockedBalance(); unlockedBalance = wallet.getUnlockedBalance();
refreshBalance(); refreshBalance();
String sync = ""; String sync;
if (!activityCallback.hasBoundService()) if (!activityCallback.hasBoundService())
throw new IllegalStateException("WalletService not bound."); throw new IllegalStateException("WalletService not bound.");
Wallet.ConnectionStatus daemonConnected = activityCallback.getConnectionStatus(); Wallet.ConnectionStatus daemonConnected = activityCallback.getConnectionStatus();
@@ -483,7 +493,7 @@ public class WalletFragment extends Fragment
long getDaemonHeight(); //mBoundService.getDaemonHeight(); long getDaemonHeight(); //mBoundService.getDaemonHeight();
void onSendRequest(); void onSendRequest(View view);
void onTxDetailsRequest(View view, TransactionInfo info); void onTxDetailsRequest(View view, TransactionInfo info);
@@ -497,7 +507,7 @@ public class WalletFragment extends Fragment
String getTxKey(String txId); String getTxKey(String txId);
void onWalletReceive(); void onWalletReceive(View view);
boolean hasWallet(); boolean hasWallet();
@@ -511,7 +521,7 @@ public class WalletFragment extends Fragment
} }
@Override @Override
public void onAttach(Context context) { public void onAttach(@NonNull Context context) {
super.onAttach(context); super.onAttach(context);
if (context instanceof Listener) { if (context instanceof Listener) {
this.activityCallback = (Listener) context; this.activityCallback = (Listener) context;
@@ -524,9 +534,10 @@ public class WalletFragment extends Fragment
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
setExitTransition(null);
setReenterTransition(null);
Timber.d("onResume()"); Timber.d("onResume()");
activityCallback.setTitle(walletTitle, walletSubtitle); activityCallback.setTitle(walletTitle, walletSubtitle);
//activityCallback.setToolbarButton(Toolbar.BUTTON_CLOSE); // TODO: Close button somewhere else
activityCallback.setToolbarButton(Toolbar.BUTTON_NONE); activityCallback.setToolbarButton(Toolbar.BUTTON_NONE);
setProgress(syncProgress); setProgress(syncProgress);
setProgress(syncText); setProgress(syncText);
@@ -554,7 +565,7 @@ public class WalletFragment extends Fragment
//TODO figure out why gunther disappears on return from send although he is still set //TODO figure out why gunther disappears on return from send although he is still set
if (enable) { if (enable) {
if (streetGunther == null) if (streetGunther == null)
streetGunther = ContextCompat.getDrawable(getContext(), R.drawable.ic_gunther_streetmode); streetGunther = ContextCompat.getDrawable(requireContext(), R.drawable.ic_gunther_streetmode);
ivStreetGunther.setImageDrawable(streetGunther); ivStreetGunther.setImageDrawable(streetGunther);
} else } else
ivStreetGunther.setImageDrawable(null); ivStreetGunther.setImageDrawable(null);

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2018 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.data;
import java.util.regex.Pattern;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@RequiredArgsConstructor
@ToString
@EqualsAndHashCode
public class Subaddress implements Comparable<Subaddress> {
@Getter
final private int accountIndex;
@Getter
final private int addressIndex;
@Getter
final private String address;
@Getter
private final String label;
@Getter
@Setter
private long amount;
@Override
public int compareTo(Subaddress another) { // newer is <
final int compareAccountIndex = another.accountIndex - accountIndex;
if (compareAccountIndex == 0)
return another.addressIndex - addressIndex;
return compareAccountIndex;
}
public String getSquashedAddress() {
return address.substring(0, 8) + "…" + address.substring(address.length() - 8);
}
public static final Pattern DEFAULT_LABEL_FORMATTER = Pattern.compile("^[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}:[0-9]{2}:[0-9]{2}$");
public String getDisplayLabel() {
if (label.isEmpty() || (DEFAULT_LABEL_FORMATTER.matcher(label).matches()))
return ("#" + addressIndex);
else
return label;
}
}

View File

@@ -21,6 +21,7 @@ import android.os.Parcelable;
import com.m2049r.xmrwallet.model.PendingTransaction; import com.m2049r.xmrwallet.model.PendingTransaction;
import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.util.Helper;
// https://stackoverflow.com/questions/2139134/how-to-send-an-object-from-one-android-activity-to-another-using-intents // https://stackoverflow.com/questions/2139134/how-to-send-an-object-from-one-android-activity-to-another-using-intents
public class TxData implements Parcelable { public class TxData implements Parcelable {
@@ -54,7 +55,7 @@ public class TxData implements Parcelable {
} }
public double getAmountAsDouble() { public double getAmountAsDouble() {
return 1.0 * amount / 1000000000000L; return 1.0 * amount / Helper.ONE_XMR;
} }
public int getMixin() { public int getMixin() {

View File

@@ -117,32 +117,34 @@ public class SendAddressWizardFragment extends SendWizardFragment {
View view = inflater.inflate(R.layout.fragment_send_address, container, false); View view = inflater.inflate(R.layout.fragment_send_address, container, false);
tvXmrTo = view.findViewById(R.id.tvXmrTo); if (Helper.ALLOW_SHIFT) {
tvXmrTo = view.findViewById(R.id.tvXmrTo);
ibCrypto = new HashMap<>(); ibCrypto = new HashMap<>();
for (Crypto crypto : Crypto.values()) { for (Crypto crypto : Crypto.values()) {
final ImageButton button = view.findViewById(crypto.getButtonId()); final ImageButton button = view.findViewById(crypto.getButtonId());
ibCrypto.put(crypto, button); ibCrypto.put(crypto, button);
button.setOnClickListener(v -> { button.setOnClickListener(v -> {
if (possibleCryptos.contains(crypto)) { if (possibleCryptos.contains(crypto)) {
selectedCrypto = crypto; selectedCrypto = crypto;
updateCryptoButtons(false); updateCryptoButtons(false);
} else {
// show help what to do:
if (button.getId() != R.id.ibXMR) {
final String name = getResources().getStringArray(R.array.cryptos)[crypto.ordinal()];
final String symbol = getCryptoForButton(button).getSymbol();
tvXmrTo.setText(Html.fromHtml(getString(R.string.info_xmrto_help, name, symbol)));
tvXmrTo.setVisibility(View.VISIBLE);
} else { } else {
tvXmrTo.setText(Html.fromHtml(getString(R.string.info_xmrto_help_xmr))); // show help what to do:
tvXmrTo.setVisibility(View.VISIBLE); if (button.getId() != R.id.ibXMR) {
final String name = getResources().getStringArray(R.array.cryptos)[crypto.ordinal()];
final String symbol = getCryptoForButton(button).getSymbol();
tvXmrTo.setText(Html.fromHtml(getString(R.string.info_xmrto_help, name, symbol)));
tvXmrTo.setVisibility(View.VISIBLE);
} else {
tvXmrTo.setText(Html.fromHtml(getString(R.string.info_xmrto_help_xmr)));
tvXmrTo.setVisibility(View.VISIBLE);
}
} }
} });
}); }
updateCryptoButtons(true);
} else {
view.findViewById(R.id.llExchange).setVisibility(View.GONE);
} }
updateCryptoButtons(true);
etAddress = view.findViewById(R.id.etAddress); etAddress = view.findViewById(R.id.etAddress);
etAddress.getEditText().setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); etAddress.getEditText().setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
etAddress.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() { etAddress.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
@@ -181,6 +183,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
selectedCrypto = Crypto.XMR; selectedCrypto = Crypto.XMR;
sendListener.setMode(SendFragment.Mode.XMR); sendListener.setMode(SendFragment.Mode.XMR);
} }
if (!Helper.ALLOW_SHIFT) return;
if ((selectedCrypto == null) && isEthAddress(address)) { if ((selectedCrypto == null) && isEthAddress(address)) {
Timber.d("isEthAddress"); Timber.d("isEthAddress");
possibleCryptos.add(Crypto.ETH); possibleCryptos.add(Crypto.ETH);
@@ -285,6 +288,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
} }
private void updateCryptoButtons(boolean noAddress) { private void updateCryptoButtons(boolean noAddress) {
if (!Helper.ALLOW_SHIFT) return;
for (Crypto crypto : Crypto.values()) { for (Crypto crypto : Crypto.values()) {
if (crypto == selectedCrypto) { if (crypto == selectedCrypto) {
selectedCrypto(crypto); selectedCrypto(crypto);
@@ -345,7 +349,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
private boolean checkAddress() { private boolean checkAddress() {
boolean ok = checkAddressNoError(); boolean ok = checkAddressNoError();
if (!ok) { if (possibleCryptos.isEmpty()) {
etAddress.setError(getString(R.string.send_address_invalid)); etAddress.setError(getString(R.string.send_address_invalid));
} else { } else {
etAddress.setError(null); etAddress.setError(null);
@@ -447,7 +451,11 @@ public class SendAddressWizardFragment extends SendWizardFragment {
BarcodeData barcodeData = sendListener.getBarcodeData(); BarcodeData barcodeData = sendListener.getBarcodeData();
if (barcodeData != null) { if (barcodeData != null) {
Timber.d("GOT DATA"); Timber.d("GOT DATA");
if (!Helper.ALLOW_SHIFT && (barcodeData.asset != Crypto.XMR)) {
Timber.d("BUT ONLY XMR SUPPORTED");
barcodeData = null;
sendListener.setBarcodeData(barcodeData);
}
if (barcodeData.address != null) { if (barcodeData.address != null) {
etAddress.getEditText().setText(barcodeData.address); etAddress.getEditText().setText(barcodeData.address);
possibleCryptos.clear(); possibleCryptos.clear();
@@ -458,7 +466,8 @@ public class SendAddressWizardFragment extends SendWizardFragment {
possibleCryptos.add(barcodeData.asset); possibleCryptos.add(barcodeData.asset);
selectedCrypto = barcodeData.asset; selectedCrypto = barcodeData.asset;
} }
updateCryptoButtons(false); if (Helper.ALLOW_SHIFT)
updateCryptoButtons(false);
if (checkAddress()) { if (checkAddress()) {
if (barcodeData.security == BarcodeData.Security.OA_NO_DNSSEC) if (barcodeData.security == BarcodeData.Security.OA_NO_DNSSEC)
etAddress.setError(getString(R.string.send_address_no_dnssec)); etAddress.setError(getString(R.string.send_address_no_dnssec));

View File

@@ -139,7 +139,7 @@ public class SendAmountWizardFragment extends SendWizardFragment {
Timber.d("onResumeFragment()"); Timber.d("onResumeFragment()");
Helper.showKeyboard(getActivity()); Helper.showKeyboard(getActivity());
final long funds = getTotalFunds(); final long funds = getTotalFunds();
maxFunds = 1.0 * funds / 1000000000000L; maxFunds = 1.0 * funds / Helper.ONE_XMR;
if (!sendListener.getActivityCallback().isStreetMode()) { if (!sendListener.getActivityCallback().isStreetMode()) {
tvFunds.setText(getString(R.string.send_available, tvFunds.setText(getString(R.string.send_available,
Wallet.getDisplayAmount(funds))); Wallet.getDisplayAmount(funds)));

View File

@@ -34,6 +34,7 @@ import com.m2049r.xmrwallet.service.shift.ShiftException;
import com.m2049r.xmrwallet.service.shift.sideshift.api.QueryOrderParameters; import com.m2049r.xmrwallet.service.shift.sideshift.api.QueryOrderParameters;
import com.m2049r.xmrwallet.service.shift.sideshift.api.SideShiftApi; import com.m2049r.xmrwallet.service.shift.sideshift.api.SideShiftApi;
import com.m2049r.xmrwallet.service.shift.sideshift.network.SideShiftApiImpl; import com.m2049r.xmrwallet.service.shift.sideshift.network.SideShiftApiImpl;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.OkHttpHelper; import com.m2049r.xmrwallet.util.OkHttpHelper;
import com.m2049r.xmrwallet.util.ServiceHelper; import com.m2049r.xmrwallet.util.ServiceHelper;
import com.m2049r.xmrwallet.widget.ExchangeOtherEditText; import com.m2049r.xmrwallet.widget.ExchangeOtherEditText;
@@ -89,7 +90,6 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
return view; return view;
} }
@Override @Override
public boolean onValidateFields() { public boolean onValidateFields() {
Timber.i(maxBtc + "/" + minBtc); Timber.i(maxBtc + "/" + minBtc);
@@ -179,7 +179,7 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
tvXmrToParms.setText(xmrParmText); tvXmrToParms.setText(xmrParmText);
final long funds = getTotalFunds(); final long funds = getTotalFunds();
double availableXmr = 1.0 * funds / 1000000000000L; double availableXmr = 1.0 * funds / Helper.ONE_XMR;
String availBtcString; String availBtcString;
String availXmrString; String availXmrString;

View File

@@ -359,40 +359,22 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
} }
private RequestQuote xmrtoQuote = null; private RequestQuote xmrtoQuote = null;
private int stageARetries = 0;
private final int RETRIES = 3;
private double stageAPrice = 0;
private void processStageA(final RequestQuote requestQuote) { private void processStageA(final RequestQuote requestQuote) {
Timber.d("processCreateOrder %s", requestQuote.getId()); Timber.d("processCreateOrder %s", requestQuote.getId());
TxDataBtc txDataBtc = (TxDataBtc) sendListener.getTxData(); TxDataBtc txDataBtc = (TxDataBtc) sendListener.getTxData();
// verify the BTC amount is correct (price can change and we can only specify XMR amount) // verify the BTC amount is correct
if (requestQuote.getBtcAmount() != txDataBtc.getBtcAmount()) { if (requestQuote.getBtcAmount() != txDataBtc.getBtcAmount()) {
if (--stageARetries <= 0) { Timber.d("Failed to get quote");
Timber.d("Failed to get quote"); getView().post(() -> showStageError(ShiftError.Error.SERVICE.toString(),
getView().post(() -> getString(R.string.shift_noquote),
showStageError(ShiftError.Error.SERVICE.toString(), getString(R.string.shift_checkamount)));
getString(R.string.shift_noquote), return; // just stop for now
getString(R.string.shift_checkamount)));
return; // just stop for now
}
if (stageAPrice == requestQuote.getPrice()) {
// same price but different BTC amount - something else is wrong (e.g. too many decimals)
Timber.d("Price unchanged");
getView().post(() ->
showStageError(ShiftError.Error.SERVICE.toString(),
getString(R.string.shift_noquote),
getString(R.string.shift_checkamount)));
return; // just stop for now
}
stageAPrice = requestQuote.getPrice();
// recalc XMR and try again
txDataBtc.setAmount(txDataBtc.getBtcAmount() / requestQuote.getPrice());
getView().post(this::stageAOneShot);
return; // stageA will run in the main thread
} }
xmrtoQuote = requestQuote; xmrtoQuote = requestQuote;
txDataBtc.setAmount(xmrtoQuote.getXmrAmount());
getView().post(() -> { getView().post(() -> {
// show data from the actual quote as that is what is used to
NumberFormat df = NumberFormat.getInstance(Locale.US); NumberFormat df = NumberFormat.getInstance(Locale.US);
df.setMaximumFractionDigits(12); df.setMaximumFractionDigits(12);
final String btcAmount = df.format(xmrtoQuote.getBtcAmount()); final String btcAmount = df.format(xmrtoQuote.getBtcAmount());
@@ -438,18 +420,12 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
} }
private void stageA() { private void stageA() {
stageARetries = RETRIES;
stageAOneShot();
}
private void stageAOneShot() {
if (!isResumed) return; if (!isResumed) return;
Timber.d("Request Quote"); Timber.d("Request Quote");
xmrtoQuote = null; xmrtoQuote = null;
xmrtoOrder = null; xmrtoOrder = null;
showProgress(1, getString(R.string.label_send_progress_xmrto_create)); showProgress(1, getString(R.string.label_send_progress_xmrto_create));
TxDataBtc txDataBtc = (TxDataBtc) sendListener.getTxData(); TxDataBtc txDataBtc = (TxDataBtc) sendListener.getTxData();
stageAPrice = 0;
ShiftCallback<RequestQuote> callback = new ShiftCallback<RequestQuote>() { ShiftCallback<RequestQuote> callback = new ShiftCallback<RequestQuote>() {
@Override @Override
@@ -473,7 +449,7 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
} }
}; };
getXmrToApi().requestQuote(txDataBtc.getAmountAsDouble(), callback); getXmrToApi().requestQuote(txDataBtc.getBtcAmount(), callback);
} }
private CreateOrder xmrtoOrder = null; private CreateOrder xmrtoOrder = null;

View File

@@ -36,6 +36,7 @@ import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter; import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.viewpager.widget.ViewPager; import androidx.viewpager.widget.ViewPager;
import com.google.android.material.transition.MaterialContainerTransform;
import com.m2049r.xmrwallet.OnBackPressedListener; import com.m2049r.xmrwallet.OnBackPressedListener;
import com.m2049r.xmrwallet.OnUriScannedListener; import com.m2049r.xmrwallet.OnUriScannedListener;
import com.m2049r.xmrwallet.R; import com.m2049r.xmrwallet.R;
@@ -49,10 +50,12 @@ import com.m2049r.xmrwallet.layout.SpendViewPager;
import com.m2049r.xmrwallet.model.PendingTransaction; import com.m2049r.xmrwallet.model.PendingTransaction;
import com.m2049r.xmrwallet.util.Helper; import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.Notice; import com.m2049r.xmrwallet.util.Notice;
import com.m2049r.xmrwallet.util.ThemeHelper;
import com.m2049r.xmrwallet.widget.DotBar; import com.m2049r.xmrwallet.widget.DotBar;
import com.m2049r.xmrwallet.widget.Toolbar; import com.m2049r.xmrwallet.widget.Toolbar;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.Objects;
import timber.log.Timber; import timber.log.Timber;
@@ -63,7 +66,7 @@ public class SendFragment extends Fragment
SendSuccessWizardFragment.Listener, SendSuccessWizardFragment.Listener,
OnBackPressedListener, OnUriScannedListener { OnBackPressedListener, OnUriScannedListener {
final static public int MIXIN = 10; final static public int MIXIN = 0;
private Listener activityCallback; private Listener activityCallback;
@@ -309,7 +312,7 @@ public class SendFragment extends Fragment
SparseArray<WeakReference<SendWizardFragment>> myFragments = new SparseArray<>(); SparseArray<WeakReference<SendWizardFragment>> myFragments = new SparseArray<>();
public SpendPagerAdapter(FragmentManager fm) { public SpendPagerAdapter(FragmentManager fm) {
super(fm); super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
} }
public void addSuccess() { public void addSuccess() {
@@ -538,14 +541,18 @@ public class SendFragment extends Fragment
enableNavigation(); enableNavigation();
} }
@Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setHasOptionsMenu(true); setHasOptionsMenu(true);
final MaterialContainerTransform transform = new MaterialContainerTransform();
transform.setDrawingViewId(R.id.fragment_container);
transform.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
transform.setAllContainerColors(ThemeHelper.getThemedColor(getContext(), android.R.attr.colorBackground));
setSharedElementEnterTransition(transform);
} }
@Override @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.send_menu, menu); inflater.inflate(R.menu.send_menu, menu);
super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater);
} }

View File

@@ -48,7 +48,7 @@ public class NodeInfoAdapter extends RecyclerView.Adapter<NodeInfoAdapter.ViewHo
public interface OnInteractionListener { public interface OnInteractionListener {
void onInteraction(View view, NodeInfo item); void onInteraction(View view, NodeInfo item);
void onLongInteraction(View view, NodeInfo item); boolean onLongInteraction(View view, NodeInfo item);
} }
private final List<NodeInfo> nodeItems = new ArrayList<>(); private final List<NodeInfo> nodeItems = new ArrayList<>();
@@ -213,10 +213,10 @@ public class NodeInfoAdapter extends RecyclerView.Adapter<NodeInfoAdapter.ViewHo
if (listener != null) { if (listener != null) {
int position = getAdapterPosition(); // gets item position int position = getAdapterPosition(); // gets item position
if (position != RecyclerView.NO_POSITION) { // Check if an item was deleted, but the user clicked it before the UI removed it if (position != RecyclerView.NO_POSITION) { // Check if an item was deleted, but the user clicked it before the UI removed it
listener.onLongInteraction(view, nodeItems.get(position)); return listener.onLongInteraction(view, nodeItems.get(position));
} }
} }
return true; return false;
} }
} }

View File

@@ -0,0 +1,164 @@
/*
* 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.layout;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.data.Subaddress;
import com.m2049r.xmrwallet.util.Helper;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import timber.log.Timber;
public class SubaddressInfoAdapter extends RecyclerView.Adapter<SubaddressInfoAdapter.ViewHolder> {
public interface OnInteractionListener {
void onInteraction(View view, Subaddress item);
boolean onLongInteraction(View view, Subaddress item);
}
private final List<Subaddress> items;
private final OnInteractionListener listener;
Context context;
public SubaddressInfoAdapter(Context context, OnInteractionListener listener) {
this.context = context;
this.items = new ArrayList<>();
this.listener = listener;
}
private static class SubaddressInfoDiff extends DiffCallback<Subaddress> {
public SubaddressInfoDiff(List<Subaddress> oldList, List<Subaddress> newList) {
super(oldList, newList);
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return mOldList.get(oldItemPosition).getAddress().equals(mNewList.get(newItemPosition).getAddress());
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return mOldList.get(oldItemPosition).equals(mNewList.get(newItemPosition));
}
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_subaddress, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.bind(position);
}
@Override
public int getItemCount() {
return items.size();
}
public Subaddress getItem(int position) {
return items.get(position);
}
public void setInfos(List<Subaddress> newItems) {
if (newItems == null) {
newItems = new ArrayList<>();
Timber.d("setInfos null");
} else {
Timber.d("setInfos %s", newItems.size());
}
Collections.sort(newItems);
final DiffCallback<Subaddress> diffCallback = new SubaddressInfoDiff(items, newItems);
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);
items.clear();
items.addAll(newItems);
diffResult.dispatchUpdatesTo(this);
}
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
final TextView tvName;
final TextView tvAddress;
final TextView tvAmount;
Subaddress item;
ViewHolder(View itemView) {
super(itemView);
tvName = itemView.findViewById(R.id.tvName);
tvAddress = itemView.findViewById(R.id.tvAddress);
tvAmount = itemView.findViewById(R.id.tx_amount);
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
}
void bind(int position) {
item = getItem(position);
itemView.setTransitionName(context.getString(R.string.subaddress_item_transition_name, item.getAddressIndex()));
final String label = item.getDisplayLabel();
final String address = context.getString(R.string.subbaddress_info_subtitle,
item.getAddressIndex(), item.getSquashedAddress());
tvName.setText(label.isEmpty() ? address : label);
tvAddress.setText(address);
final long amount = item.getAmount();
if (amount > 0)
tvAmount.setText(context.getString(R.string.tx_list_amount_positive,
Helper.getDisplayAmount(amount, Helper.DISPLAY_DIGITS_INFO)));
else
tvAmount.setText("");
}
@Override
public void onClick(View view) {
if (listener != null) {
int position = getAdapterPosition(); // gets item position
if (position != RecyclerView.NO_POSITION) { // Check if an item was deleted, but the user clicked it before the UI removed it
listener.onInteraction(view, getItem(position));
}
}
}
@Override
public boolean onLongClick(View view) {
if (listener != null) {
int position = getAdapterPosition(); // gets item position
if (position != RecyclerView.NO_POSITION) { // Check if an item was deleted, but the user clicked it before the UI removed it
return listener.onLongInteraction(view, getItem(position));
}
}
return true;
}
}
}

View File

@@ -17,6 +17,8 @@
package com.m2049r.xmrwallet.layout; package com.m2049r.xmrwallet.layout;
import android.content.Context; import android.content.Context;
import android.text.Html;
import android.text.Spanned;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -28,6 +30,7 @@ import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.DiffUtil; import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.progressindicator.CircularProgressIndicator;
import com.m2049r.xmrwallet.R; import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.data.Crypto; import com.m2049r.xmrwallet.data.Crypto;
import com.m2049r.xmrwallet.data.UserNotes; import com.m2049r.xmrwallet.data.UserNotes;
@@ -46,8 +49,7 @@ import java.util.TimeZone;
import timber.log.Timber; import timber.log.Timber;
public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfoAdapter.ViewHolder> { public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfoAdapter.ViewHolder> {
private final static SimpleDateFormat DATETIME_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm");
private final SimpleDateFormat DATETIME_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm");
private final int outboundColour; private final int outboundColour;
private final int inboundColour; private final int inboundColour;
@@ -76,6 +78,10 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
DATETIME_FORMATTER.setTimeZone(tz); DATETIME_FORMATTER.setTimeZone(tz);
} }
public boolean needsTransactionUpdateOnNewBlock() {
return (infoItems.size() > 0) && !infoItems.get(0).isConfirmed();
}
private static class TransactionInfoDiff extends DiffCallback<TransactionInfo> { private static class TransactionInfoDiff extends DiffCallback<TransactionInfo> {
public TransactionInfoDiff(List<TransactionInfo> oldList, List<TransactionInfo> newList) { public TransactionInfoDiff(List<TransactionInfo> oldList, List<TransactionInfo> newList) {
@@ -94,6 +100,8 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
return (oldItem.direction == newItem.direction) return (oldItem.direction == newItem.direction)
&& (oldItem.isPending == newItem.isPending) && (oldItem.isPending == newItem.isPending)
&& (oldItem.isFailed == newItem.isFailed) && (oldItem.isFailed == newItem.isFailed)
&& (oldItem.confirmations == newItem.confirmations)
&& (oldItem.subaddressLabel.equals(newItem.subaddressLabel))
&& (Objects.equals(oldItem.notes, newItem.notes)); && (Objects.equals(oldItem.notes, newItem.notes));
} }
} }
@@ -147,6 +155,8 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
final TextView tvFee; final TextView tvFee;
final TextView tvPaymentId; final TextView tvPaymentId;
final TextView tvDateTime; final TextView tvDateTime;
final CircularProgressIndicator pbConfirmations;
final TextView tvConfirmations;
TransactionInfo infoItem; TransactionInfo infoItem;
ViewHolder(View itemView) { ViewHolder(View itemView) {
@@ -156,6 +166,9 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
tvFee = itemView.findViewById(R.id.tx_fee); tvFee = itemView.findViewById(R.id.tx_fee);
tvPaymentId = itemView.findViewById(R.id.tx_paymentid); tvPaymentId = itemView.findViewById(R.id.tx_paymentid);
tvDateTime = itemView.findViewById(R.id.tx_datetime); tvDateTime = itemView.findViewById(R.id.tx_datetime);
pbConfirmations = itemView.findViewById(R.id.pbConfirmations);
pbConfirmations.setMax(TransactionInfo.CONFIRMATION);
tvConfirmations = itemView.findViewById(R.id.tvConfirmations);
} }
private String getDateTime(long time) { private String getDateTime(long time) {
@@ -180,7 +193,7 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
ivTxType.setVisibility(View.GONE); ivTxType.setVisibility(View.GONE);
} }
} else { } else {
ivTxType.setVisibility(View.GONE); // gives us more space for the amount ivTxType.setVisibility(View.GONE);
} }
String displayAmount = Helper.getDisplayAmount(infoItem.amount, Helper.DISPLAY_DIGITS_INFO); String displayAmount = Helper.getDisplayAmount(infoItem.amount, Helper.DISPLAY_DIGITS_INFO);
@@ -203,22 +216,55 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
this.tvFee.setText(context.getString(R.string.tx_list_failed_text)); this.tvFee.setText(context.getString(R.string.tx_list_failed_text));
tvFee.setVisibility(View.VISIBLE); tvFee.setVisibility(View.VISIBLE);
setTxColour(failedColour); setTxColour(failedColour);
pbConfirmations.setVisibility(View.GONE);
tvConfirmations.setVisibility(View.GONE);
} else if (infoItem.isPending) { } else if (infoItem.isPending) {
setTxColour(pendingColour); setTxColour(pendingColour);
pbConfirmations.setVisibility(View.GONE);
pbConfirmations.setIndeterminate(true);
pbConfirmations.setVisibility(View.VISIBLE);
tvConfirmations.setVisibility(View.GONE);
} else if (infoItem.direction == TransactionInfo.Direction.Direction_In) { } else if (infoItem.direction == TransactionInfo.Direction.Direction_In) {
setTxColour(inboundColour); setTxColour(inboundColour);
if (!infoItem.isConfirmed()) {
pbConfirmations.setVisibility(View.VISIBLE);
final int confirmations = (int) infoItem.confirmations;
pbConfirmations.setProgressCompat(confirmations, true);
final String confCount = Integer.toString(confirmations);
tvConfirmations.setText(confCount);
if (confCount.length() == 1) // we only have space for character in the progress circle
tvConfirmations.setVisibility(View.VISIBLE);
else
tvConfirmations.setVisibility(View.GONE);
} else {
pbConfirmations.setVisibility(View.GONE);
tvConfirmations.setVisibility(View.GONE);
}
} else { } else {
setTxColour(outboundColour); setTxColour(outboundColour);
pbConfirmations.setVisibility(View.GONE);
tvConfirmations.setVisibility(View.GONE);
} }
String tag = null;
String info = "";
if ((infoItem.addressIndex != 0) && (infoItem.direction == TransactionInfo.Direction.Direction_In))
tag = infoItem.getDisplayLabel();
if ((userNotes.note.isEmpty())) { if ((userNotes.note.isEmpty())) {
this.tvPaymentId.setText(infoItem.paymentId.equals("0000000000000000") ? if (!infoItem.paymentId.equals("0000000000000000")) {
(infoItem.subaddress != 0 ? info = infoItem.paymentId;
(context.getString(R.string.tx_subaddress, infoItem.subaddress)) : }
"") :
infoItem.paymentId);
} else { } else {
this.tvPaymentId.setText(userNotes.note); info = userNotes.note;
}
if (tag == null) {
tvPaymentId.setText(info);
} else {
Spanned label = Html.fromHtml(context.getString(R.string.tx_details_notes,
Integer.toHexString(ContextCompat.getColor(context, R.color.monerujoGreen) & 0xFFFFFF),
Integer.toHexString(ContextCompat.getColor(context, R.color.monerujoBackground) & 0xFFFFFF),
tag, info.isEmpty() ? "" : ("&nbsp; " + info)));
tvPaymentId.setText(label);
} }
this.tvDateTime.setText(getDateTime(infoItem.timestamp)); this.tvDateTime.setText(getDateTime(infoItem.timestamp));

View File

@@ -74,7 +74,7 @@ public class WalletInfoAdapter extends RecyclerView.Adapter<WalletInfoAdapter.Vi
@Override @Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return mOldList.get(oldItemPosition).name.equals(mNewList.get(newItemPosition).name); return mOldList.get(oldItemPosition).getName().equals(mNewList.get(newItemPosition).getName());
} }
@Override @Override
@@ -86,9 +86,9 @@ public class WalletInfoAdapter extends RecyclerView.Adapter<WalletInfoAdapter.Vi
@NonNull @NonNull
@Override @Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()) return new ViewHolder(
.inflate(R.layout.item_wallet, parent, false); LayoutInflater.from(parent.getContext()).inflate(R.layout.item_wallet, parent, false)
return new ViewHolder(view); );
} }
@Override @Override
@@ -122,7 +122,6 @@ public class WalletInfoAdapter extends RecyclerView.Adapter<WalletInfoAdapter.Vi
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
final TextView tvName; final TextView tvName;
final TextView tvAddress;
final ImageButton ibOptions; final ImageButton ibOptions;
WalletManager.WalletInfo infoItem; WalletManager.WalletInfo infoItem;
boolean popupOpen = false; boolean popupOpen = false;
@@ -130,7 +129,6 @@ public class WalletInfoAdapter extends RecyclerView.Adapter<WalletInfoAdapter.Vi
ViewHolder(View itemView) { ViewHolder(View itemView) {
super(itemView); super(itemView);
tvName = itemView.findViewById(R.id.tvName); tvName = itemView.findViewById(R.id.tvName);
tvAddress = itemView.findViewById(R.id.tvAddress);
ibOptions = itemView.findViewById(R.id.ibOptions); ibOptions = itemView.findViewById(R.id.ibOptions);
ibOptions.setOnClickListener(view -> { ibOptions.setOnClickListener(view -> {
if (popupOpen) return; if (popupOpen) return;
@@ -160,8 +158,7 @@ public class WalletInfoAdapter extends RecyclerView.Adapter<WalletInfoAdapter.Vi
void bind(int position) { void bind(int position) {
infoItem = infoItems.get(position); infoItem = infoItems.get(position);
tvName.setText(infoItem.name); tvName.setText(infoItem.getName());
tvAddress.setText(infoItem.address.substring(0, 16) + "...");
} }
@Override @Override

View File

@@ -71,18 +71,18 @@ public class TransactionHistory {
// } // }
public void refresh() { public void refresh() {
List<TransactionInfo> t = refreshJ(); List<TransactionInfo> transactionInfos = refreshJ();
Timber.d("refreshed %d", t.size()); Timber.d("refreshed %d", transactionInfos.size());
for (Iterator<TransactionInfo> iterator = t.iterator(); iterator.hasNext(); ) { for (Iterator<TransactionInfo> iterator = transactionInfos.iterator(); iterator.hasNext(); ) {
TransactionInfo info = iterator.next(); TransactionInfo info = iterator.next();
if (info.account != accountIndex) { if (info.accountIndex != accountIndex) {
iterator.remove(); iterator.remove();
Timber.d("removed %s", info.hash); Timber.d("removed %s", info.hash);
} else { } else {
Timber.d("kept %s", info.hash); Timber.d("kept %s", info.hash);
} }
} }
transactions = t; transactions = transactionInfos;
} }
private native List<TransactionInfo> refreshJ(); private native List<TransactionInfo> refreshJ();

View File

@@ -19,13 +19,19 @@ package com.m2049r.xmrwallet.model;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import com.m2049r.xmrwallet.data.Subaddress;
import java.util.List; import java.util.List;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
// this is not the TransactionInfo from the API as that is owned by the TransactionHistory // this is not the TransactionInfo from the API as that is owned by the TransactionHistory
// this is a POJO for the TransactionInfoAdapter // this is a POJO for the TransactionInfoAdapter
public class TransactionInfo implements Parcelable, Comparable<TransactionInfo> { public class TransactionInfo implements Parcelable, Comparable<TransactionInfo> {
static final String TAG = "TransactionInfo"; public static final int CONFIRMATION = 10; // blocks
@RequiredArgsConstructor
public enum Direction { public enum Direction {
Direction_In(0), Direction_In(0),
Direction_Out(1); Direction_Out(1);
@@ -40,15 +46,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
return null; return null;
} }
public int getValue() { @Getter
return value; private final int value;
}
private int value;
Direction(int value) {
this.value = value;
}
} }
public Direction direction; public Direction direction;
@@ -60,8 +59,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
public String hash; public String hash;
public long timestamp; public long timestamp;
public String paymentId; public String paymentId;
public int account; public int accountIndex;
public int subaddress; public int addressIndex;
public long confirmations; public long confirmations;
public String subaddressLabel; public String subaddressLabel;
public List<Transfer> transfers; public List<Transfer> transfers;
@@ -80,8 +79,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
String hash, String hash,
long timestamp, long timestamp,
String paymentId, String paymentId,
int account, int accountIndex,
int subaddress, int addressIndex,
long confirmations, long confirmations,
String subaddressLabel, String subaddressLabel,
List<Transfer> transfers) { List<Transfer> transfers) {
@@ -94,13 +93,24 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
this.hash = hash; this.hash = hash;
this.timestamp = timestamp; this.timestamp = timestamp;
this.paymentId = paymentId; this.paymentId = paymentId;
this.account = account; this.accountIndex = accountIndex;
this.subaddress = subaddress; this.addressIndex = addressIndex;
this.confirmations = confirmations; this.confirmations = confirmations;
this.subaddressLabel = subaddressLabel; this.subaddressLabel = subaddressLabel;
this.transfers = transfers; this.transfers = transfers;
} }
public boolean isConfirmed() {
return confirmations >= CONFIRMATION;
}
public String getDisplayLabel() {
if (subaddressLabel.isEmpty() || (Subaddress.DEFAULT_LABEL_FORMATTER.matcher(subaddressLabel).matches()))
return ("#" + addressIndex);
else
return subaddressLabel;
}
public String toString() { public String toString() {
return direction + "@" + blockheight + " " + amount; return direction + "@" + blockheight + " " + amount;
} }
@@ -116,8 +126,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
out.writeString(hash); out.writeString(hash);
out.writeLong(timestamp); out.writeLong(timestamp);
out.writeString(paymentId); out.writeString(paymentId);
out.writeInt(account); out.writeInt(accountIndex);
out.writeInt(subaddress); out.writeInt(addressIndex);
out.writeLong(confirmations); out.writeLong(confirmations);
out.writeString(subaddressLabel); out.writeString(subaddressLabel);
out.writeList(transfers); out.writeList(transfers);
@@ -146,8 +156,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
hash = in.readString(); hash = in.readString();
timestamp = in.readLong(); timestamp = in.readLong();
paymentId = in.readString(); paymentId = in.readString();
account = in.readInt(); accountIndex = in.readInt();
subaddress = in.readInt(); addressIndex = in.readInt();
confirmations = in.readLong(); confirmations = in.readLong();
subaddressLabel = in.readString(); subaddressLabel = in.readString();
transfers = in.readArrayList(Transfer.class.getClassLoader()); transfers = in.readArrayList(Transfer.class.getClassLoader());

View File

@@ -19,6 +19,7 @@ package com.m2049r.xmrwallet.model;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.m2049r.xmrwallet.data.Subaddress;
import com.m2049r.xmrwallet.data.TxData; import com.m2049r.xmrwallet.data.TxData;
import java.io.File; import java.io.File;
@@ -26,6 +27,8 @@ import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.Locale; import java.util.Locale;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import timber.log.Timber; import timber.log.Timber;
public class Wallet { public class Wallet {
@@ -104,10 +107,14 @@ public class Wallet {
this.accountIndex = accountIndex; this.accountIndex = accountIndex;
} }
@RequiredArgsConstructor
@Getter
public enum Device { public enum Device {
Device_Undefined, Device_Undefined(0, 0),
Device_Software, Device_Software(50, 200),
Device_Ledger Device_Ledger(5, 20);
private final int accountLookahead;
private final int subaddressLookahead;
} }
public enum StatusEnum { public enum StatusEnum {
@@ -160,6 +167,24 @@ public class Wallet {
private native String getAddressJ(int accountIndex, int addressIndex); private native String getAddressJ(int accountIndex, int addressIndex);
public Subaddress getSubaddressObject(int accountIndex, int subAddressIndex) {
return new Subaddress(accountIndex, subAddressIndex,
getSubaddress(subAddressIndex), getSubaddressLabel(subAddressIndex));
}
public Subaddress getSubaddressObject(int subAddressIndex) {
Subaddress subaddress = getSubaddressObject(accountIndex, subAddressIndex);
long amount = 0;
for (TransactionInfo info : getHistory().getAll()) {
if ((info.addressIndex == subAddressIndex)
&& (info.direction == TransactionInfo.Direction.Direction_In)) {
amount += info.amount;
}
}
subaddress.setAmount(amount);
return subaddress;
}
public native String getPath(); public native String getPath();
public NetworkType getNetworkType() { public NetworkType getNetworkType() {
@@ -248,12 +273,14 @@ public class Wallet {
public native long getDaemonBlockChainTargetHeight(); public native long getDaemonBlockChainTargetHeight();
public native boolean isSynchronizedJ(); boolean synced = false;
public boolean isSynchronized() { public boolean isSynchronized() {
final long daemonHeight = getDaemonBlockChainHeight(); return synced;
if (daemonHeight == 0) return false; }
return isSynchronizedJ() && (getBlockChainHeight() == daemonHeight);
public void setSynchronized() {
this.synced = true;
} }
public static native String getDisplayAmount(long amount); public static native String getDisplayAmount(long amount);
@@ -284,7 +311,12 @@ public class Wallet {
public native void refreshAsync(); public native void refreshAsync();
public native void rescanBlockchainAsync(); public native void rescanBlockchainAsyncJ();
public void rescanBlockchainAsync() {
synced = false;
rescanBlockchainAsyncJ();
}
//TODO virtual void setAutoRefreshInterval(int millis) = 0; //TODO virtual void setAutoRefreshInterval(int millis) = 0;
//TODO virtual int autoRefreshInterval() const = 0; //TODO virtual int autoRefreshInterval() const = 0;
@@ -428,6 +460,11 @@ public class Wallet {
setSubaddressLabel(accountIndex, 0, label); setSubaddressLabel(accountIndex, 0, label);
} }
public void setSubaddressLabel(int addressIndex, String label) {
setSubaddressLabel(accountIndex, addressIndex, label);
getHistory().refreshWithNotes(this);
}
public native void setSubaddressLabel(int accountIndex, int addressIndex, String label); public native void setSubaddressLabel(int accountIndex, int addressIndex, String label);
public native int getNumAccounts(); public native int getNumAccounts();

View File

@@ -21,15 +21,13 @@ import com.m2049r.xmrwallet.data.Node;
import com.m2049r.xmrwallet.ledger.Ledger; import com.m2049r.xmrwallet.ledger.Ledger;
import com.m2049r.xmrwallet.util.RestoreHeight; import com.m2049r.xmrwallet.util.RestoreHeight;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileReader;
import java.io.FilenameFilter; import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import lombok.Getter;
import timber.log.Timber; import timber.log.Timber;
public class WalletManager { public class WalletManager {
@@ -103,7 +101,8 @@ public class WalletManager {
wallet.setRestoreHeight(restoreHeight); wallet.setRestoreHeight(restoreHeight);
Timber.d("Changed Restore Height from %d to %d", oldHeight, wallet.getRestoreHeight()); Timber.d("Changed Restore Height from %d to %d", oldHeight, wallet.getRestoreHeight());
wallet.setPassword(password); // this rewrites the keys file (which contains the restore height) wallet.setPassword(password); // this rewrites the keys file (which contains the restore height)
} } else
Timber.e(wallet.getStatus().toString());
return wallet; return wallet;
} }
@@ -210,46 +209,22 @@ public class WalletManager {
//public native List<String> findWallets(String path); // this does not work - some error in boost //public native List<String> findWallets(String path); // this does not work - some error in boost
public class WalletInfo implements Comparable<WalletInfo> { public class WalletInfo implements Comparable<WalletInfo> {
public File path; @Getter
public String name; final private File path;
public String address; @Getter
final private String name;
public WalletInfo(File wallet) {
path = wallet.getParentFile();
name = wallet.getName();
}
@Override @Override
public int compareTo(WalletInfo another) { public int compareTo(WalletInfo another) {
int n = name.toLowerCase().compareTo(another.name.toLowerCase()); return name.toLowerCase().compareTo(another.name.toLowerCase());
if (n != 0) {
return n;
} else { // wallet names are the same
return address.compareTo(another.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");
//Timber.d(addressFile.getAbsolutePath());
info.address = "??????";
BufferedReader addressReader = null;
try {
addressReader = new BufferedReader(new FileReader(addressFile));
info.address = addressReader.readLine();
} catch (IOException ex) {
Timber.d(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) { public List<WalletInfo> findWallets(File path) {
List<WalletInfo> wallets = new ArrayList<>(); List<WalletInfo> wallets = new ArrayList<>();
Timber.d("Scanning: %s", path.getAbsolutePath()); Timber.d("Scanning: %s", path.getAbsolutePath());
@@ -261,7 +236,7 @@ public class WalletManager {
for (int i = 0; i < found.length; i++) { for (int i = 0; i < found.length; i++) {
String filename = found[i].getName(); String filename = found[i].getName();
File f = new File(found[i].getParent(), filename.substring(0, filename.length() - 5)); // 5 is length of ".keys"+1 File f = new File(found[i].getParent(), filename.substring(0, filename.length() - 5)); // 5 is length of ".keys"+1
wallets.add(getWalletInfo(f)); wallets.add(new WalletInfo(f));
} }
return wallets; return wallets;
} }

View File

@@ -108,16 +108,16 @@ public class WalletService extends Service {
Timber.d("unconfirmedMoneyReceived() %d @ %s", amount, txId); Timber.d("unconfirmedMoneyReceived() %d @ %s", amount, txId);
} }
long lastBlockTime = 0; private long lastBlockTime = 0;
int lastTxCount = 0; private int lastTxCount = 0;
public void newBlock(long height) { public void newBlock(long height) {
Wallet wallet = getWallet(); final Wallet wallet = getWallet();
if (wallet == null) throw new IllegalStateException("No wallet!"); if (wallet == null) throw new IllegalStateException("No wallet!");
// don't flood with an update for every block ... // don't flood with an update for every block ...
if (lastBlockTime < System.currentTimeMillis() - 2000) { if (lastBlockTime < System.currentTimeMillis() - 2000) {
Timber.d("newBlock() @ %d with observer %s", height, observer);
lastBlockTime = System.currentTimeMillis(); lastBlockTime = System.currentTimeMillis();
Timber.d("newBlock() @ %d with observer %s", height, observer);
if (observer != null) { if (observer != null) {
boolean fullRefresh = false; boolean fullRefresh = false;
updateDaemonState(wallet, wallet.isSynchronized() ? height : 0); updateDaemonState(wallet, wallet.isSynchronized() ? height : 0);
@@ -145,17 +145,16 @@ public class WalletService extends Service {
updated = true; updated = true;
} }
public void refreshed() { public void refreshed() { // this means it's synced
Timber.d("refreshed()"); Timber.d("refreshed()");
Wallet wallet = getWallet(); final Wallet wallet = getWallet();
if (wallet == null) throw new IllegalStateException("No wallet!"); if (wallet == null) throw new IllegalStateException("No wallet!");
wallet.setSynchronized();
if (updated) { if (updated) {
updateDaemonState(wallet, wallet.getBlockChainHeight());
wallet.getHistory().refreshWithNotes(wallet);
if (observer != null) { if (observer != null) {
updateDaemonState(wallet, 0); updated = !observer.onRefreshed(wallet, true);
wallet.getHistory().refreshWithNotes(wallet);
if (observer != null) {
updated = !observer.onRefreshed(wallet, true);
}
} }
} }
} }

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