mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-06 02:27:11 +02:00
Compare commits
61 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
05720e63ab | ||
![]() |
cdc2b23257 | ||
![]() |
9f0e89719c | ||
![]() |
190726e61c | ||
![]() |
729fafdb48 | ||
![]() |
b7ae23ac64 | ||
![]() |
27885f2c86 | ||
![]() |
ce9046c7b5 | ||
![]() |
506e6ce017 | ||
![]() |
f07b439731 | ||
![]() |
ac70ba8424 | ||
![]() |
84ec1ef418 | ||
![]() |
b576a9de3d | ||
![]() |
148faa00e4 | ||
![]() |
e82b471c14 | ||
![]() |
9ed92e5117 | ||
![]() |
303b3aa354 | ||
![]() |
d801a50962 | ||
![]() |
9cd8a75dc6 | ||
![]() |
002dfd5d58 | ||
![]() |
54e54b2a8a | ||
![]() |
aa768596a4 | ||
![]() |
c4958f6c54 | ||
![]() |
2c2a5314d4 | ||
![]() |
669516c60b | ||
![]() |
a56a29a6c4 | ||
![]() |
4e31f47482 | ||
![]() |
c1f14f9653 | ||
![]() |
2746c52d7b | ||
![]() |
5df323bacb | ||
![]() |
776cc26377 | ||
![]() |
bdfb6a90b6 | ||
![]() |
6db44dfab1 | ||
![]() |
c68ac7db6d | ||
![]() |
e09862e940 | ||
![]() |
c7bd7469a1 | ||
![]() |
b39857fd2e | ||
![]() |
8170f823ab | ||
![]() |
38c0ead45c | ||
![]() |
1d027c1694 | ||
![]() |
45dc21fbf7 | ||
![]() |
d4f4de234a | ||
![]() |
394d5471e3 | ||
![]() |
beb098adb3 | ||
![]() |
fda370d35b | ||
![]() |
99681e1bbb | ||
![]() |
21f44380b1 | ||
![]() |
f4c1af1bb8 | ||
![]() |
c002b81ebd | ||
![]() |
d2f07ba3b6 | ||
![]() |
24fc27b09e | ||
![]() |
cf4ff856d5 | ||
![]() |
b01de1ad6e | ||
![]() |
5fb1bcb552 | ||
![]() |
b937ba38b6 | ||
![]() |
849718fdc7 | ||
![]() |
a6372f701d | ||
![]() |
12f135bb14 | ||
![]() |
16870fcbb9 | ||
![]() |
2fbd152fb3 | ||
![]() |
a7b178e024 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -15,3 +15,4 @@
|
|||||||
/app/alphaStagenet
|
/app/alphaStagenet
|
||||||
/app/prodStagenet
|
/app/prodStagenet
|
||||||
/app/.cxx
|
/app/.cxx
|
||||||
|
/monerujo.id
|
||||||
|
@@ -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
|
||||||
|
@@ -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 709
|
versionCode 1201
|
||||||
versionName "1.17.9 'Druk'"
|
versionName "2.2.1 'René'"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
@@ -112,27 +113,30 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def getId(name) {
|
static def getId(name) {
|
||||||
def Properties props = new Properties()
|
Properties props = new Properties()
|
||||||
props.load(new FileInputStream(new File('monerujo.id')))
|
props.load(new FileInputStream(new File('monerujo.id')))
|
||||||
return props[name]
|
return props[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'androidx.core:core:1.3.2'
|
implementation 'androidx.core:core:1.6.0'
|
||||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||||
implementation 'com.google.android.material:material:1.3.0'
|
implementation 'com.google.android.material:material:1.4.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.1'
|
||||||
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.1.1'
|
||||||
|
|
||||||
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 'info.guardianproject.netcipher:netcipher:2.1.0'
|
||||||
|
//implementation 'info.guardianproject.netcipher:netcipher-okhttp3:2.1.0'
|
||||||
|
implementation fileTree(dir: 'libs/classes', include: ['*.jar'])
|
||||||
implementation 'com.nulab-inc:zxcvbn:1.3.0'
|
implementation 'com.nulab-inc:zxcvbn:1.3.0'
|
||||||
|
|
||||||
implementation 'dnsjava:dnsjava:2.1.9'
|
implementation 'dnsjava:dnsjava:2.1.9'
|
||||||
@@ -140,8 +144,7 @@ dependencies {
|
|||||||
implementation 'org.slf4j:slf4j-nop:1.7.30'
|
implementation 'org.slf4j:slf4j-nop:1.7.30'
|
||||||
implementation 'com.github.brnunes:swipeablerecyclerview:1.0.2'
|
implementation 'com.github.brnunes:swipeablerecyclerview:1.0.2'
|
||||||
|
|
||||||
implementation 'com.github.aelstad:keccakj:1.1.0'
|
//noinspection GradleDependency
|
||||||
|
|
||||||
testImplementation "junit:junit:$rootProject.ext.junitVersion"
|
testImplementation "junit:junit:$rootProject.ext.junitVersion"
|
||||||
testImplementation "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
|
testImplementation "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
|
||||||
testImplementation "com.squareup.okhttp3:mockwebserver:4.9.0"
|
testImplementation "com.squareup.okhttp3:mockwebserver:4.9.0"
|
||||||
@@ -151,3 +154,4 @@ dependencies {
|
|||||||
compileOnly 'org.projectlombok:lombok:1.18.16'
|
compileOnly 'org.projectlombok:lombok:1.18.16'
|
||||||
annotationProcessor 'org.projectlombok:lombok:1.18.16'
|
annotationProcessor 'org.projectlombok:lombok:1.18.16'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4,20 +4,37 @@
|
|||||||
|
|
||||||
<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" />
|
||||||
|
|
||||||
|
<queries>
|
||||||
|
<intent>
|
||||||
|
<action android:name="org.torproject.android.intent.action.START" />
|
||||||
|
</intent>
|
||||||
|
<intent>
|
||||||
|
<action android:name="org.torproject.android.intent.action.STATUS" />
|
||||||
|
</intent>
|
||||||
|
|
||||||
|
<intent>
|
||||||
|
<action android:name="org.torproject.android.REQUEST_HS_PORT" />
|
||||||
|
</intent>
|
||||||
|
<intent>
|
||||||
|
<action android:name="org.torproject.android.REQUEST_V3_ONION_SERVICE" />
|
||||||
|
</intent>
|
||||||
|
|
||||||
|
<package android:name="org.torproject.android" />
|
||||||
|
</queries>
|
||||||
|
|
||||||
<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 +53,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"
|
||||||
|
@@ -531,6 +531,17 @@ Java_com_m2049r_xmrwallet_model_WalletManager_resolveOpenAlias(JNIEnv *env, jobj
|
|||||||
return env->NewStringUTF(resolvedAlias.c_str());
|
return env->NewStringUTF(resolvedAlias.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jboolean JNICALL
|
||||||
|
Java_com_m2049r_xmrwallet_model_WalletManager_setProxy(JNIEnv *env, jobject instance,
|
||||||
|
jstring address) {
|
||||||
|
const char *_address = env->GetStringUTFChars(address, nullptr);
|
||||||
|
bool rc =
|
||||||
|
Bitmonero::WalletManagerFactory::getWalletManager()->setProxy(std::string(_address));
|
||||||
|
env->ReleaseStringUTFChars(address, _address);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//TODO static std::tuple<bool, std::string, std::string, std::string, std::string> checkUpdates(const std::string &software, const std::string &subdir);
|
//TODO static std::tuple<bool, std::string, std::string, std::string, std::string> checkUpdates(const std::string &software, const std::string &subdir);
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
@@ -727,6 +738,16 @@ Java_com_m2049r_xmrwallet_model_Wallet_getConnectionStatusJ(JNIEnv *env, jobject
|
|||||||
//TODO virtual void setTrustedDaemon(bool arg) = 0;
|
//TODO virtual void setTrustedDaemon(bool arg) = 0;
|
||||||
//TODO virtual bool trustedDaemon() const = 0;
|
//TODO virtual bool trustedDaemon() const = 0;
|
||||||
|
|
||||||
|
JNIEXPORT jboolean JNICALL
|
||||||
|
Java_com_m2049r_xmrwallet_model_Wallet_setProxy(JNIEnv *env, jobject instance,
|
||||||
|
jstring address) {
|
||||||
|
const char *_address = env->GetStringUTFChars(address, nullptr);
|
||||||
|
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
|
||||||
|
bool rc = wallet->setProxy(std::string(_address));
|
||||||
|
env->ReleaseStringUTFChars(address, _address);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
JNIEXPORT jlong JNICALL
|
JNIEXPORT jlong JNICALL
|
||||||
Java_com_m2049r_xmrwallet_model_Wallet_getBalance(JNIEnv *env, jobject instance,
|
Java_com_m2049r_xmrwallet_model_Wallet_getBalance(JNIEnv *env, jobject instance,
|
||||||
jint accountIndex) {
|
jint accountIndex) {
|
||||||
@@ -914,7 +935,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();
|
||||||
}
|
}
|
||||||
|
@@ -77,7 +77,7 @@ public class Dispatcher implements PeerRetriever.OnGetPeers {
|
|||||||
final NodeInfo nodeInfo = retrievedPeer.getNodeInfo();
|
final NodeInfo nodeInfo = retrievedPeer.getNodeInfo();
|
||||||
Timber.d("Retrieved %s", nodeInfo);
|
Timber.d("Retrieved %s", nodeInfo);
|
||||||
if ((nodeInfo.isValid() || nodeInfo.isFavourite())) {
|
if ((nodeInfo.isValid() || nodeInfo.isFavourite())) {
|
||||||
nodeInfo.setName();
|
nodeInfo.setDefaultName();
|
||||||
rpcNodes.add(nodeInfo);
|
rpcNodes.add(nodeInfo);
|
||||||
Timber.d("RPC: %s", nodeInfo);
|
Timber.d("RPC: %s", nodeInfo);
|
||||||
// the following is not totally correct but it works (otherwise we need to
|
// the following is not totally correct but it works (otherwise we need to
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -20,7 +20,6 @@ import android.content.Context;
|
|||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.KeyEvent;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
@@ -59,7 +58,6 @@ import java.text.NumberFormat;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
@@ -219,8 +217,8 @@ public class NodeFragment extends Fragment
|
|||||||
activityCallback.setNode(nodeItem); // this marks it as selected & saves it as well
|
activityCallback.setNode(nodeItem); // this marks it as selected & saves it as well
|
||||||
nodeItem.setSelecting(false);
|
nodeItem.setSelecting(false);
|
||||||
try {
|
try {
|
||||||
Objects.requireNonNull(getActivity()).runOnUiThread(() -> nodesAdapter.allowClick(true));
|
requireActivity().runOnUiThread(() -> nodesAdapter.allowClick(true));
|
||||||
} catch (NullPointerException ex) {
|
} catch (IllegalStateException ex) {
|
||||||
// it's ok
|
// it's ok
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -228,24 +226,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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,16 +401,12 @@ public class NodeFragment extends Fragment
|
|||||||
etNodeHost.setError(getString(R.string.node_host_empty));
|
etNodeHost.setError(getString(R.string.node_host_empty));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final boolean setHostSuccess = Helper.runWithNetwork(new Helper.Action() {
|
final boolean setHostSuccess = Helper.runWithNetwork(() -> {
|
||||||
@Override
|
try {
|
||||||
public boolean run() {
|
nodeInfo.setHost(host);
|
||||||
try {
|
return true;
|
||||||
nodeInfo.setHost(host);
|
} catch (UnknownHostException ex) {
|
||||||
return true;
|
return false;
|
||||||
} catch (UnknownHostException ex) {
|
|
||||||
etNodeHost.setError(getString(R.string.node_host_unresolved));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!setHostSuccess) {
|
if (!setHostSuccess) {
|
||||||
@@ -422,14 +415,7 @@ public class NodeFragment extends Fragment
|
|||||||
}
|
}
|
||||||
etNodeHost.setError(null);
|
etNodeHost.setError(null);
|
||||||
nodeInfo.setRpcPort(port);
|
nodeInfo.setRpcPort(port);
|
||||||
// setName() may trigger reverse DNS
|
nodeInfo.setName(etNodeName.getEditText().getText().toString().trim());
|
||||||
Helper.runWithNetwork(new Helper.Action() {
|
|
||||||
@Override
|
|
||||||
public boolean run() {
|
|
||||||
nodeInfo.setName(etNodeName.getEditText().getText().toString().trim());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
nodeInfo.setUsername(etNodeUser.getEditText().getText().toString().trim());
|
nodeInfo.setUsername(etNodeUser.getEditText().getText().toString().trim());
|
||||||
nodeInfo.setPassword(etNodePass.getEditText().getText().toString()); // no trim for pw
|
nodeInfo.setPassword(etNodePass.getEditText().getText().toString()); // no trim for pw
|
||||||
return true;
|
return true;
|
||||||
@@ -533,20 +519,10 @@ public class NodeFragment extends Fragment
|
|||||||
@Override
|
@Override
|
||||||
public void onShow(final DialogInterface dialog) {
|
public void onShow(final DialogInterface dialog) {
|
||||||
Button testButton = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_NEUTRAL);
|
Button testButton = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_NEUTRAL);
|
||||||
testButton.setOnClickListener(new View.OnClickListener() {
|
testButton.setOnClickListener(view -> test());
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
test();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
|
Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
|
||||||
button.setOnClickListener(new View.OnClickListener() {
|
button.setOnClickListener(view -> apply());
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
apply();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -554,15 +530,13 @@ public class NodeFragment extends Fragment
|
|||||||
editDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
editDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
etNodePass.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
etNodePass.getEditText().setOnEditorActionListener((v, actionId, event) -> {
|
||||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
editDialog.getButton(DialogInterface.BUTTON_NEUTRAL).requestFocus();
|
||||||
editDialog.getButton(DialogInterface.BUTTON_NEUTRAL).requestFocus();
|
test();
|
||||||
test();
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -590,6 +564,7 @@ public class NodeFragment extends Fragment
|
|||||||
} else {
|
} else {
|
||||||
nodesAdapter.setNodes();
|
nodesAdapter.setNodes();
|
||||||
}
|
}
|
||||||
|
nodesAdapter.notifyItemChanged(nodeInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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
251
app/src/main/java/com/m2049r/xmrwallet/SubaddressFragment.java
Normal file
251
app/src/main/java/com/m2049r/xmrwallet/SubaddressFragment.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
@@ -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;
|
||||||
@@ -36,11 +38,15 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.core.content.ContextCompat;
|
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.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.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;
|
||||||
@@ -50,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";
|
||||||
@@ -86,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);
|
||||||
@@ -119,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,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);
|
||||||
}
|
}
|
||||||
@@ -203,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)));
|
||||||
@@ -254,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) {
|
||||||
@@ -279,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,7 +319,7 @@ public class TxFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
tvTxXmrToKey.setText(key);
|
tvTxXmrToKey.setText(key);
|
||||||
tvDestinationBtc.setText(userNotes.xmrtoDestination);
|
tvDestinationBtc.setText(userNotes.xmrtoDestination);
|
||||||
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " "+ userNotes.xmrtoCurrency);
|
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " " + userNotes.xmrtoCurrency);
|
||||||
switch (userNotes.xmrtoTag) {
|
switch (userNotes.xmrtoTag) {
|
||||||
case "xmrto":
|
case "xmrto":
|
||||||
tvXmrToSupport.setVisibility(View.GONE);
|
tvXmrToSupport.setVisibility(View.GONE);
|
||||||
@@ -329,6 +348,11 @@ public class TxFragment extends Fragment {
|
|||||||
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
|
||||||
@@ -340,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);
|
||||||
|
|
||||||
@@ -354,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
|
||||||
@@ -378,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
File diff suppressed because it is too large
Load Diff
@@ -21,14 +21,17 @@ import android.content.Context;
|
|||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.BuildConfig;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.model.NetworkType;
|
import com.m2049r.xmrwallet.model.NetworkType;
|
||||||
import com.m2049r.xmrwallet.util.LocaleHelper;
|
import com.m2049r.xmrwallet.util.LocaleHelper;
|
||||||
|
import com.m2049r.xmrwallet.util.NetCipherHelper;
|
||||||
import com.m2049r.xmrwallet.util.NightmodeHelper;
|
import com.m2049r.xmrwallet.util.NightmodeHelper;
|
||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class XmrWalletApplication extends Application {
|
public class XmrWalletApplication extends Application {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
@@ -36,7 +39,10 @@ public class XmrWalletApplication extends Application {
|
|||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Timber.plant(new Timber.DebugTree());
|
Timber.plant(new Timber.DebugTree());
|
||||||
}
|
}
|
||||||
|
|
||||||
NightmodeHelper.setPreferredNightmode(this);
|
NightmodeHelper.setPreferredNightmode(this);
|
||||||
|
|
||||||
|
NetCipherHelper.createInstance(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -45,7 +51,7 @@ public class XmrWalletApplication extends Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConfigurationChanged(Configuration configuration) {
|
public void onConfigurationChanged(@NonNull Configuration configuration) {
|
||||||
super.onConfigurationChanged(configuration);
|
super.onConfigurationChanged(configuration);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
LocaleHelper.updateSystemDefaultLocale(configuration.getLocales().get(0));
|
LocaleHelper.updateSystemDefaultLocale(configuration.getLocales().get(0));
|
||||||
|
@@ -28,7 +28,11 @@ public enum DefaultNodes {
|
|||||||
SUPPORTXMR("node.supportxmr.com:18081"),
|
SUPPORTXMR("node.supportxmr.com:18081"),
|
||||||
HASHVAULT("nodes.hashvault.pro:18081"),
|
HASHVAULT("nodes.hashvault.pro:18081"),
|
||||||
MONEROWORLD("node.moneroworld.com:18089"),
|
MONEROWORLD("node.moneroworld.com:18089"),
|
||||||
XMRTW("opennode.xmr-tw.org:18089");
|
XMRTW("opennode.xmr-tw.org:18089"),
|
||||||
|
MONERUJO_ONION("monerujods7mbghwe6cobdr6ujih6c22zu5rl7zshmizz2udf7v7fsad.onion:18081/mainnet/monerujo.onion"),
|
||||||
|
Criminales78("56wl7y2ebhamkkiza4b7il4mrzwtyvpdym7bm2bkg3jrei2je646k3qd.onion:18089/mainnet/Criminales78.onion"),
|
||||||
|
xmrfail("mxcd4577fldb3ppzy7obmmhnu3tf57gbcbd4qhwr2kxyjj2qi3dnbfqd.onion:18081/mainnet/xmrfail.onion"),
|
||||||
|
boldsuck("6dsdenp6vjkvqzy4wzsnzn6wixkdzihx3khiumyzieauxuxslmcaeiad.onion:18081/mainnet/boldsuck.onion");
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final String uri;
|
private final String uri;
|
||||||
|
@@ -18,6 +18,7 @@ package com.m2049r.xmrwallet.data;
|
|||||||
|
|
||||||
import com.m2049r.xmrwallet.model.NetworkType;
|
import com.m2049r.xmrwallet.model.NetworkType;
|
||||||
import com.m2049r.xmrwallet.model.WalletManager;
|
import com.m2049r.xmrwallet.model.WalletManager;
|
||||||
|
import com.m2049r.xmrwallet.util.OnionHelper;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
@@ -35,11 +36,63 @@ public class Node {
|
|||||||
static public final String STAGENET = "stagenet";
|
static public final String STAGENET = "stagenet";
|
||||||
static public final String TESTNET = "testnet";
|
static public final String TESTNET = "testnet";
|
||||||
|
|
||||||
|
static class Address {
|
||||||
|
final private InetAddress inet;
|
||||||
|
final private String onion;
|
||||||
|
|
||||||
|
public boolean isOnion() {
|
||||||
|
return onion != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHostName() {
|
||||||
|
if (inet != null) {
|
||||||
|
return inet.getHostName();
|
||||||
|
} else {
|
||||||
|
return onion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHostAddress() {
|
||||||
|
if (inet != null) {
|
||||||
|
return inet.getHostAddress();
|
||||||
|
} else {
|
||||||
|
return onion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Address(InetAddress address, String onion) {
|
||||||
|
this.inet = address;
|
||||||
|
this.onion = onion;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Address of(InetAddress address) {
|
||||||
|
return new Address(address, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Address of(String host) throws UnknownHostException {
|
||||||
|
if (OnionHelper.isOnionHost(host)) {
|
||||||
|
return new Address(null, host);
|
||||||
|
} else {
|
||||||
|
return new Address(InetAddress.getByName(host), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return getHostAddress().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
return (other instanceof Address) && (getHostAddress().equals(((Address) other).getHostAddress()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private String name = null;
|
private String name = null;
|
||||||
@Getter
|
@Getter
|
||||||
final private NetworkType networkType;
|
final private NetworkType networkType;
|
||||||
InetAddress hostAddress;
|
Address hostAddress;
|
||||||
@Getter
|
@Getter
|
||||||
private String host;
|
private String host;
|
||||||
@Getter
|
@Getter
|
||||||
@@ -74,6 +127,10 @@ public class Node {
|
|||||||
&& (networkType == anotherNode.networkType));
|
&& (networkType == anotherNode.networkType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isOnion() {
|
||||||
|
return hostAddress.isOnion();
|
||||||
|
}
|
||||||
|
|
||||||
static public Node fromString(String nodeString) {
|
static public Node fromString(String nodeString) {
|
||||||
try {
|
try {
|
||||||
return new Node(nodeString);
|
return new Node(nodeString);
|
||||||
@@ -205,7 +262,7 @@ public class Node {
|
|||||||
// constructor used for created nodes from retrieved peer lists
|
// constructor used for created nodes from retrieved peer lists
|
||||||
public Node(InetSocketAddress socketAddress) {
|
public Node(InetSocketAddress socketAddress) {
|
||||||
this();
|
this();
|
||||||
this.hostAddress = socketAddress.getAddress();
|
this.hostAddress = Address.of(socketAddress.getAddress());
|
||||||
this.host = socketAddress.getHostString();
|
this.host = socketAddress.getHostString();
|
||||||
this.rpcPort = 0; // unknown
|
this.rpcPort = 0; // unknown
|
||||||
this.levinPort = socketAddress.getPort();
|
this.levinPort = socketAddress.getPort();
|
||||||
@@ -225,17 +282,25 @@ public class Node {
|
|||||||
if ((host == null) || (host.isEmpty()))
|
if ((host == null) || (host.isEmpty()))
|
||||||
throw new UnknownHostException("loopback not supported (yet?)");
|
throw new UnknownHostException("loopback not supported (yet?)");
|
||||||
this.host = host;
|
this.host = host;
|
||||||
this.hostAddress = InetAddress.getByName(host);
|
this.hostAddress = Address.of(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setName() {
|
public void setDefaultName() {
|
||||||
if (name == null)
|
if (name != null) return;
|
||||||
this.name = hostAddress.getHostName();
|
String nodeName = hostAddress.getHostName();
|
||||||
|
if (hostAddress.isOnion()) {
|
||||||
|
nodeName = nodeName.substring(0, nodeName.length() - ".onion".length());
|
||||||
|
if (nodeName.length() > 16) {
|
||||||
|
nodeName = nodeName.substring(0, 8) + "…" + nodeName.substring(nodeName.length() - 6);
|
||||||
|
}
|
||||||
|
nodeName = nodeName + ".onion";
|
||||||
|
}
|
||||||
|
this.name = nodeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
if ((name == null) || (name.isEmpty()))
|
if ((name == null) || (name.isEmpty()))
|
||||||
this.name = hostAddress.getHostName();
|
setDefaultName();
|
||||||
else
|
else
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
@@ -16,14 +16,19 @@
|
|||||||
|
|
||||||
package com.m2049r.xmrwallet.data;
|
package com.m2049r.xmrwallet.data;
|
||||||
|
|
||||||
import com.burgstaller.okhttp.AuthenticationCacheInterceptor;
|
import android.content.Context;
|
||||||
import com.burgstaller.okhttp.CachingAuthenticatorDecorator;
|
import android.text.Html;
|
||||||
import com.burgstaller.okhttp.digest.CachingAuthenticator;
|
import android.text.Spanned;
|
||||||
import com.burgstaller.okhttp.digest.Credentials;
|
import android.widget.TextView;
|
||||||
import com.burgstaller.okhttp.digest.DigestAuthenticator;
|
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import com.m2049r.levin.scanner.LevinPeer;
|
import com.m2049r.levin.scanner.LevinPeer;
|
||||||
|
import com.m2049r.xmrwallet.R;
|
||||||
|
import com.m2049r.xmrwallet.util.NetCipherHelper;
|
||||||
|
import com.m2049r.xmrwallet.util.NetCipherHelper.Request;
|
||||||
import com.m2049r.xmrwallet.util.NodePinger;
|
import com.m2049r.xmrwallet.util.NodePinger;
|
||||||
import com.m2049r.xmrwallet.util.OkHttpHelper;
|
import com.m2049r.xmrwallet.util.ThemeHelper;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
@@ -32,17 +37,12 @@ import java.io.IOException;
|
|||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
import java.util.Calendar;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import okhttp3.HttpUrl;
|
import okhttp3.HttpUrl;
|
||||||
import okhttp3.MediaType;
|
|
||||||
import okhttp3.OkHttpClient;
|
|
||||||
import okhttp3.Request;
|
|
||||||
import okhttp3.RequestBody;
|
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
import okhttp3.ResponseBody;
|
import okhttp3.ResponseBody;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
@@ -94,7 +94,7 @@ public class NodeInfo extends Node {
|
|||||||
synchronized public SocketAddress getLevinSocketAddress() {
|
synchronized public SocketAddress getLevinSocketAddress() {
|
||||||
if (levinSocketAddress == null) {
|
if (levinSocketAddress == null) {
|
||||||
// use default peer port if not set - very few peers use nonstandard port
|
// use default peer port if not set - very few peers use nonstandard port
|
||||||
levinSocketAddress = new InetSocketAddress(hostAddress, getDefaultLevinPort());
|
levinSocketAddress = new InetSocketAddress(hostAddress.getHostAddress(), getDefaultLevinPort());
|
||||||
}
|
}
|
||||||
return levinSocketAddress;
|
return levinSocketAddress;
|
||||||
}
|
}
|
||||||
@@ -180,7 +180,7 @@ public class NodeInfo extends Node {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int HTTP_TIMEOUT = OkHttpHelper.HTTP_TIMEOUT;
|
private static final int HTTP_TIMEOUT = 1000; //ms
|
||||||
public static final double PING_GOOD = HTTP_TIMEOUT / 3.0; //ms
|
public static final double PING_GOOD = HTTP_TIMEOUT / 3.0; //ms
|
||||||
public static final double PING_MEDIUM = 2 * PING_GOOD; //ms
|
public static final double PING_MEDIUM = 2 * PING_GOOD; //ms
|
||||||
public static final double PING_BAD = HTTP_TIMEOUT;
|
public static final double PING_BAD = HTTP_TIMEOUT;
|
||||||
@@ -196,32 +196,29 @@ public class NodeInfo extends Node {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Request rpcServiceRequest(int port) {
|
||||||
|
final HttpUrl url = new HttpUrl.Builder()
|
||||||
|
.scheme("http")
|
||||||
|
.host(getHost())
|
||||||
|
.port(port)
|
||||||
|
.addPathSegment("json_rpc")
|
||||||
|
.build();
|
||||||
|
final String json = "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"getlastblockheader\"}";
|
||||||
|
return new Request(url, json, getUsername(), getPassword());
|
||||||
|
}
|
||||||
|
|
||||||
private boolean testRpcService(int port) {
|
private boolean testRpcService(int port) {
|
||||||
Timber.d("Testing %s", toNodeString());
|
Timber.d("Testing %s", toNodeString());
|
||||||
clear();
|
clear();
|
||||||
|
if (hostAddress.isOnion() && !NetCipherHelper.isTor()) {
|
||||||
|
tested = true; // sortof
|
||||||
|
responseCode = 418; // I'm a teapot - or I need an Onion - who knows
|
||||||
|
return false; // autofail
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
OkHttpClient client = OkHttpHelper.getEagerClient();
|
|
||||||
if (!getUsername().isEmpty()) {
|
|
||||||
final DigestAuthenticator authenticator =
|
|
||||||
new DigestAuthenticator(new Credentials(getUsername(), getPassword()));
|
|
||||||
final Map<String, CachingAuthenticator> authCache = new ConcurrentHashMap<>();
|
|
||||||
client = client.newBuilder()
|
|
||||||
.authenticator(new CachingAuthenticatorDecorator(authenticator, authCache))
|
|
||||||
.addInterceptor(new AuthenticationCacheInterceptor(authCache))
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
HttpUrl url = new HttpUrl.Builder()
|
|
||||||
.scheme("http")
|
|
||||||
.host(getHostAddress())
|
|
||||||
.port(port)
|
|
||||||
.addPathSegment("json_rpc")
|
|
||||||
.build();
|
|
||||||
final RequestBody reqBody = RequestBody
|
|
||||||
.create(MediaType.parse("application/json"),
|
|
||||||
"{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"getlastblockheader\"}");
|
|
||||||
Request request = OkHttpHelper.getPostRequest(url, reqBody);
|
|
||||||
long ta = System.nanoTime();
|
long ta = System.nanoTime();
|
||||||
try (Response response = client.newCall(request).execute()) {
|
try (Response response = rpcServiceRequest(port).execute()) {
|
||||||
|
Timber.d("%s: %s", response.code(), response.request().url());
|
||||||
responseTime = (System.nanoTime() - ta) / 1000000.0;
|
responseTime = (System.nanoTime() - ta) / 1000000.0;
|
||||||
responseCode = response.code();
|
responseCode = response.code();
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
@@ -243,7 +240,7 @@ public class NodeInfo extends Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException | JSONException ex) {
|
} catch (IOException | JSONException ex) {
|
||||||
Timber.d(ex);
|
Timber.d("EX: %s", ex.getMessage()); //TODO: do something here (show error?)
|
||||||
} finally {
|
} finally {
|
||||||
tested = true;
|
tested = true;
|
||||||
}
|
}
|
||||||
@@ -264,4 +261,43 @@ public class NodeInfo extends Node {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static public final int STALE_NODE_HOURS = 2;
|
||||||
|
|
||||||
|
public void showInfo(TextView view, String info, boolean isError) {
|
||||||
|
final Context ctx = view.getContext();
|
||||||
|
final Spanned text = Html.fromHtml(ctx.getString(R.string.status,
|
||||||
|
Integer.toHexString(ContextCompat.getColor(ctx, R.color.monerujoGreen) & 0xFFFFFF),
|
||||||
|
Integer.toHexString(ContextCompat.getColor(ctx, R.color.monerujoBackground) & 0xFFFFFF),
|
||||||
|
(hostAddress.isOnion() ? " .onion " : ""), " " + info));
|
||||||
|
view.setText(text);
|
||||||
|
if (isError)
|
||||||
|
view.setTextColor(ThemeHelper.getThemedColor(ctx, R.attr.colorError));
|
||||||
|
else
|
||||||
|
view.setTextColor(ThemeHelper.getThemedColor(ctx, R.attr.colorPrimary));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showInfo(TextView view) {
|
||||||
|
if (!isTested()) {
|
||||||
|
showInfo(view, "", false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final Context ctx = view.getContext();
|
||||||
|
final long now = Calendar.getInstance().getTimeInMillis() / 1000;
|
||||||
|
final long secs = (now - timestamp);
|
||||||
|
final long mins = secs / 60;
|
||||||
|
final long hours = mins / 60;
|
||||||
|
final long days = hours / 24;
|
||||||
|
String info;
|
||||||
|
if (mins < 2) {
|
||||||
|
info = ctx.getString(R.string.node_updated_now, secs);
|
||||||
|
} else if (hours < 2) {
|
||||||
|
info = ctx.getString(R.string.node_updated_mins, mins);
|
||||||
|
} else if (days < 2) {
|
||||||
|
info = ctx.getString(R.string.node_updated_hours, hours);
|
||||||
|
} else {
|
||||||
|
info = ctx.getString(R.string.node_updated_days, days);
|
||||||
|
}
|
||||||
|
showInfo(view, info, hours >= STALE_NODE_HOURS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
63
app/src/main/java/com/m2049r/xmrwallet/data/Subaddress.java
Normal file
63
app/src/main/java/com/m2049r/xmrwallet/data/Subaddress.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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() {
|
||||||
|
@@ -17,13 +17,17 @@
|
|||||||
package com.m2049r.xmrwallet.dialog;
|
package com.m2049r.xmrwallet.dialog;
|
||||||
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.DialogInterface;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Html;
|
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.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.fragment.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
@@ -31,15 +35,20 @@ import androidx.fragment.app.FragmentTransaction;
|
|||||||
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
import com.m2049r.xmrwallet.R;
|
import com.m2049r.xmrwallet.R;
|
||||||
|
import com.m2049r.xmrwallet.util.NetCipherHelper;
|
||||||
|
|
||||||
public class HelpFragment extends DialogFragment {
|
public class HelpFragment extends DialogFragment {
|
||||||
static final String TAG = "HelpFragment";
|
static final String TAG = "HelpFragment";
|
||||||
private static final String HELP_ID = "HELP_ID";
|
private static final String HELP_ID = "HELP_ID";
|
||||||
|
private static final String TOR_BUTTON = "TOR";
|
||||||
|
|
||||||
public static HelpFragment newInstance(int helpResourceId) {
|
public static HelpFragment newInstance(int helpResourceId) {
|
||||||
HelpFragment fragment = new HelpFragment();
|
HelpFragment fragment = new HelpFragment();
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putInt(HELP_ID, helpResourceId);
|
bundle.putInt(HELP_ID, helpResourceId);
|
||||||
|
// a hack for the tor button
|
||||||
|
if (helpResourceId == R.string.help_tor)
|
||||||
|
bundle.putInt(TOR_BUTTON, 7);
|
||||||
fragment.setArguments(bundle);
|
fragment.setArguments(bundle);
|
||||||
return fragment;
|
return fragment;
|
||||||
}
|
}
|
||||||
@@ -54,27 +63,53 @@ public class HelpFragment extends DialogFragment {
|
|||||||
HelpFragment.newInstance(helpResourceId).show(ft, TAG);
|
HelpFragment.newInstance(helpResourceId).show(ft, TAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Spanned getHtml(String html, double textSize) {
|
||||||
|
final Html.ImageGetter imageGetter = source -> {
|
||||||
|
final int imageId = getResources().getIdentifier(source.replace("/", ""), "drawable", requireActivity().getPackageName());
|
||||||
|
// Don't die if we don't find the image - use a heart instead
|
||||||
|
final Drawable drawable = ContextCompat.getDrawable(requireActivity(), imageId > 0 ? imageId : R.drawable.ic_favorite_24dp);
|
||||||
|
final double f = textSize / drawable.getIntrinsicHeight();
|
||||||
|
drawable.setBounds(0, 0, (int) (f * drawable.getIntrinsicWidth()), (int) textSize);
|
||||||
|
return drawable;
|
||||||
|
};
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
return Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY, imageGetter, null);
|
||||||
|
} else {
|
||||||
|
return Html.fromHtml(html, imageGetter, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_help, null);
|
final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_help, null);
|
||||||
|
|
||||||
int helpId = 0;
|
int helpId = 0;
|
||||||
|
boolean torButton = false;
|
||||||
Bundle arguments = getArguments();
|
Bundle arguments = getArguments();
|
||||||
if (arguments != null) {
|
if (arguments != null) {
|
||||||
helpId = arguments.getInt(HELP_ID);
|
helpId = arguments.getInt(HELP_ID);
|
||||||
|
torButton = arguments.getInt(TOR_BUTTON) > 0;
|
||||||
}
|
}
|
||||||
|
final TextView helpTv = view.findViewById(R.id.tvHelp);
|
||||||
if (helpId > 0)
|
if (helpId > 0)
|
||||||
((TextView) view.findViewById(R.id.tvHelp)).setText(Html.fromHtml(getString(helpId)));
|
helpTv.setText(getHtml(getString(helpId), helpTv.getTextSize()));
|
||||||
|
|
||||||
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity())
|
MaterialAlertDialogBuilder builder =
|
||||||
.setView(view)
|
new MaterialAlertDialogBuilder(requireActivity())
|
||||||
.setNegativeButton(R.string.help_ok,
|
.setView(view);
|
||||||
new DialogInterface.OnClickListener() {
|
if (torButton) {
|
||||||
@Override
|
builder.setNegativeButton(R.string.help_nok,
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
(dialog, id) -> dialog.dismiss())
|
||||||
|
.setPositiveButton(R.string.help_getorbot,
|
||||||
|
(dialog, id) -> {
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
}
|
NetCipherHelper.getInstance().installOrbot(requireActivity());
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
builder.setNegativeButton(R.string.help_ok,
|
||||||
|
(dialog, id) -> dialog.dismiss());
|
||||||
|
}
|
||||||
return builder.create();
|
return builder.create();
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -22,6 +22,7 @@ import android.os.Bundle;
|
|||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
|
import android.text.Spanned;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.util.Patterns;
|
import android.util.Patterns;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
@@ -91,6 +92,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
private TextInputLayout etAddress;
|
private TextInputLayout etAddress;
|
||||||
private TextInputLayout etNotes;
|
private TextInputLayout etNotes;
|
||||||
private TextView tvXmrTo;
|
private TextView tvXmrTo;
|
||||||
|
private TextView tvTor;
|
||||||
private Map<Crypto, ImageButton> ibCrypto;
|
private Map<Crypto, ImageButton> ibCrypto;
|
||||||
final private Set<Crypto> possibleCryptos = new HashSet<>();
|
final private Set<Crypto> possibleCryptos = new HashSet<>();
|
||||||
private Crypto selectedCrypto = null;
|
private Crypto selectedCrypto = null;
|
||||||
@@ -117,29 +119,39 @@ 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);
|
||||||
|
|
||||||
|
tvTor = view.findViewById(R.id.tvTor);
|
||||||
tvXmrTo = view.findViewById(R.id.tvXmrTo);
|
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);
|
if (Helper.ALLOW_SHIFT || (crypto == Crypto.XMR)) {
|
||||||
button.setOnClickListener(v -> {
|
ibCrypto.put(crypto, button);
|
||||||
if (possibleCryptos.contains(crypto)) {
|
button.setOnClickListener(v -> {
|
||||||
selectedCrypto = crypto;
|
if (possibleCryptos.contains(crypto)) {
|
||||||
updateCryptoButtons(false);
|
selectedCrypto = crypto;
|
||||||
} else {
|
updateCryptoButtons(false);
|
||||||
// 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);
|
||||||
|
tvTor.setVisibility(View.INVISIBLE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
} else {
|
||||||
|
button.setImageResource(crypto.getIconDisabledId());
|
||||||
|
button.setImageAlpha(128);
|
||||||
|
button.setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!Helper.ALLOW_SHIFT) {
|
||||||
|
tvTor.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
updateCryptoButtons(true);
|
updateCryptoButtons(true);
|
||||||
|
|
||||||
@@ -181,6 +193,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 +298,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 +359,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 +461,12 @@ 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);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (barcodeData.address != null) {
|
if (barcodeData.address != null) {
|
||||||
etAddress.getEditText().setText(barcodeData.address);
|
etAddress.getEditText().setText(barcodeData.address);
|
||||||
possibleCryptos.clear();
|
possibleCryptos.clear();
|
||||||
@@ -458,7 +477,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));
|
||||||
|
@@ -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)));
|
||||||
|
@@ -34,7 +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.OkHttpHelper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
import com.m2049r.xmrwallet.util.ServiceHelper;
|
import com.m2049r.xmrwallet.util.ServiceHelper;
|
||||||
import com.m2049r.xmrwallet.widget.ExchangeOtherEditText;
|
import com.m2049r.xmrwallet.widget.ExchangeOtherEditText;
|
||||||
import com.m2049r.xmrwallet.widget.SendProgressView;
|
import com.m2049r.xmrwallet.widget.SendProgressView;
|
||||||
@@ -89,7 +89,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 +178,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;
|
||||||
@@ -255,8 +254,7 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
|||||||
if (xmrToApi == null) {
|
if (xmrToApi == null) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (xmrToApi == null) {
|
if (xmrToApi == null) {
|
||||||
xmrToApi = new SideShiftApiImpl(OkHttpHelper.getOkHttpClient(),
|
xmrToApi = new SideShiftApiImpl(ServiceHelper.getXmrToBaseUrl());
|
||||||
ServiceHelper.getXmrToBaseUrl());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -37,7 +37,6 @@ import com.m2049r.xmrwallet.service.shift.sideshift.api.RequestQuote;
|
|||||||
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.Helper;
|
||||||
import com.m2049r.xmrwallet.util.OkHttpHelper;
|
|
||||||
import com.m2049r.xmrwallet.util.ServiceHelper;
|
import com.m2049r.xmrwallet.util.ServiceHelper;
|
||||||
import com.m2049r.xmrwallet.widget.SendProgressView;
|
import com.m2049r.xmrwallet.widget.SendProgressView;
|
||||||
|
|
||||||
@@ -359,40 +358,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 +419,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 +448,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;
|
||||||
@@ -567,8 +542,7 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
|
|||||||
if (xmrToApi == null) {
|
if (xmrToApi == null) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (xmrToApi == null) {
|
if (xmrToApi == null) {
|
||||||
xmrToApi = new SideShiftApiImpl(OkHttpHelper.getOkHttpClient(),
|
xmrToApi = new SideShiftApiImpl(ServiceHelper.getXmrToBaseUrl());
|
||||||
ServiceHelper.getXmrToBaseUrl());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -39,7 +39,6 @@ import com.m2049r.xmrwallet.service.shift.sideshift.api.QueryOrderStatus;
|
|||||||
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.Helper;
|
||||||
import com.m2049r.xmrwallet.util.OkHttpHelper;
|
|
||||||
import com.m2049r.xmrwallet.util.ServiceHelper;
|
import com.m2049r.xmrwallet.util.ServiceHelper;
|
||||||
|
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
@@ -245,8 +244,7 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
|
|||||||
if (xmrToApi == null) {
|
if (xmrToApi == null) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (xmrToApi == null) {
|
if (xmrToApi == null) {
|
||||||
xmrToApi = new SideShiftApiImpl(OkHttpHelper.getOkHttpClient(),
|
xmrToApi = new SideShiftApiImpl(ServiceHelper.getXmrToBaseUrl());
|
||||||
ServiceHelper.getXmrToBaseUrl());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user