mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-03 08:23:04 +02:00
Compare commits
33 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c956f38899 | ||
![]() |
db68f517d3 | ||
![]() |
d4b293af80 | ||
![]() |
f7bbfc2fac | ||
![]() |
e08964749e | ||
![]() |
a05fa9d177 | ||
![]() |
7fe2fbe37d | ||
![]() |
40e30fed08 | ||
![]() |
320c7865ff | ||
![]() |
5e8cf8010e | ||
![]() |
e671fa19e0 | ||
![]() |
20d5b9a100 | ||
![]() |
5d489a634b | ||
![]() |
59b6f484fd | ||
![]() |
ecaa49d67d | ||
![]() |
d2dc53599e | ||
![]() |
4d8b26f97f | ||
![]() |
581c76e7be | ||
![]() |
6f66862870 | ||
![]() |
dd92f7bb36 | ||
![]() |
46808d306b | ||
![]() |
20503d2cbd | ||
![]() |
604691ca7e | ||
![]() |
1b626ba2b0 | ||
![]() |
0ed7bdfcee | ||
![]() |
524c3dd79f | ||
![]() |
197dffeae1 | ||
![]() |
cdb29bbc2e | ||
![]() |
7b96baeca7 | ||
![]() |
0712efec78 | ||
![]() |
341df6c6a3 | ||
![]() |
ab8fb82c1b | ||
![]() |
22d9173cea |
@@ -3,13 +3,11 @@ jobs:
|
||||
build:
|
||||
working_directory: ~/code
|
||||
docker:
|
||||
- image: circleci/android:api-28-ndk
|
||||
- image: cimg/android:2022.03-ndk
|
||||
environment:
|
||||
JVM_OPTS: -Xmx3200m
|
||||
steps:
|
||||
- checkout
|
||||
- run: yes | sdkmanager --licenses || exit 0
|
||||
- run: yes | sdkmanager --update || exit 0
|
||||
- restore_cache:
|
||||
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
|
||||
- run:
|
||||
|
@@ -1,15 +1,15 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
compileSdkVersion 31
|
||||
buildToolsVersion '30.0.3'
|
||||
ndkVersion '17.2.4988734'
|
||||
defaultConfig {
|
||||
applicationId "com.m2049r.xmrwallet"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
versionCode 1201
|
||||
versionName "2.2.1 'René'"
|
||||
targetSdkVersion 31
|
||||
versionCode 1403
|
||||
versionName "2.4.3 'Baldaŭ'"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
@@ -111,6 +111,7 @@ android {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
namespace 'com.m2049r.xmrwallet'
|
||||
}
|
||||
|
||||
static def getId(name) {
|
||||
@@ -120,38 +121,39 @@ static def getId(name) {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.core:core:1.6.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
implementation 'com.google.android.material:material:1.4.0'
|
||||
implementation 'androidx.core:core:1.7.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.4.1'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||
implementation 'androidx.cardview:cardview:1.0.0'
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
|
||||
implementation 'androidx.preference:preference:1.2.0'
|
||||
|
||||
implementation 'com.google.android.material:material:1.6.0'
|
||||
|
||||
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
|
||||
implementation "com.squareup.okhttp3:okhttp:4.9.0"
|
||||
implementation "io.github.rburgst:okhttp-digest:2.5"
|
||||
implementation "com.jakewharton.timber:timber:4.7.1"
|
||||
implementation "com.squareup.okhttp3:okhttp:4.9.3"
|
||||
implementation "io.github.rburgst:okhttp-digest:2.6"
|
||||
implementation "com.jakewharton.timber:timber:5.0.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.5.2'
|
||||
|
||||
implementation 'dnsjava:dnsjava:2.1.9'
|
||||
implementation 'org.jitsi:dnssecjava:1.2.0'
|
||||
implementation 'org.slf4j:slf4j-nop:1.7.30'
|
||||
implementation 'org.slf4j:slf4j-nop:1.7.36'
|
||||
implementation 'com.github.brnunes:swipeablerecyclerview:1.0.2'
|
||||
|
||||
//noinspection GradleDependency
|
||||
testImplementation "junit:junit:$rootProject.ext.junitVersion"
|
||||
testImplementation "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
|
||||
testImplementation "com.squareup.okhttp3:mockwebserver:4.9.0"
|
||||
testImplementation 'org.json:json:20180813'
|
||||
testImplementation 'net.jodah:concurrentunit:0.4.4'
|
||||
testImplementation "junit:junit:4.13.2"
|
||||
testImplementation "org.mockito:mockito-all:1.10.19"
|
||||
testImplementation "com.squareup.okhttp3:mockwebserver:4.9.3"
|
||||
testImplementation 'org.json:json:20211205'
|
||||
testImplementation 'net.jodah:concurrentunit:0.4.6'
|
||||
|
||||
compileOnly 'org.projectlombok:lombok:1.18.16'
|
||||
annotationProcessor 'org.projectlombok:lombok:1.18.16'
|
||||
compileOnly 'org.projectlombok:lombok:1.18.22'
|
||||
annotationProcessor 'org.projectlombok:lombok:1.18.22'
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.m2049r.xmrwallet">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
@@ -18,7 +17,6 @@
|
||||
<intent>
|
||||
<action android:name="org.torproject.android.intent.action.STATUS" />
|
||||
</intent>
|
||||
|
||||
<intent>
|
||||
<action android:name="org.torproject.android.REQUEST_HS_PORT" />
|
||||
</intent>
|
||||
@@ -36,15 +34,17 @@
|
||||
android:label="@string/app_name"
|
||||
android:preserveLegacyExternalStorage="true"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/MyMaterialTheme"
|
||||
android:theme="@style/MyMaterialThemeClassic"
|
||||
android:usesCleartextTraffic="true">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="orientation|keyboardHidden|uiMode"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
@@ -56,24 +56,25 @@
|
||||
android:screenOrientation="behind" />
|
||||
<activity
|
||||
android:name=".LoginActivity"
|
||||
android:configChanges="orientation|keyboardHidden|uiMode"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="locked">
|
||||
<intent-filter>
|
||||
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:label="@string/app_name">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<data android:scheme="monero" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:label="@string/app_name">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<data android:scheme="bitcoin" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
@@ -106,4 +107,5 @@
|
||||
android:resource="@xml/filepaths" />
|
||||
</provider>
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
</manifest>
|
@@ -229,7 +229,7 @@ std::vector<std::string> java2cpp(JNIEnv *env, jobject arrayList) {
|
||||
return result;
|
||||
}
|
||||
|
||||
jobject cpp2java(JNIEnv *env, const std::vector<std::string>& vector) {
|
||||
jobject cpp2java(JNIEnv *env, const std::vector<std::string> &vector) {
|
||||
|
||||
jmethodID java_util_ArrayList_ = env->GetMethodID(class_ArrayList, "<init>", "(I)V");
|
||||
jmethodID java_util_ArrayList_add = env->GetMethodID(class_ArrayList, "add",
|
||||
@@ -301,12 +301,13 @@ Java_com_m2049r_xmrwallet_model_WalletManager_openWalletJ(JNIEnv *env, jobject i
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_com_m2049r_xmrwallet_model_WalletManager_recoveryWalletJ(JNIEnv *env, jobject instance,
|
||||
jstring path, jstring password,
|
||||
jstring mnemonic,
|
||||
jstring mnemonic, jstring offset,
|
||||
jint networkType,
|
||||
jlong restoreHeight) {
|
||||
const char *_path = env->GetStringUTFChars(path, nullptr);
|
||||
const char *_password = env->GetStringUTFChars(password, nullptr);
|
||||
const char *_mnemonic = env->GetStringUTFChars(mnemonic, nullptr);
|
||||
const char *_offset = env->GetStringUTFChars(offset, nullptr);
|
||||
Monero::NetworkType _networkType = static_cast<Monero::NetworkType>(networkType);
|
||||
|
||||
Bitmonero::Wallet *wallet =
|
||||
@@ -315,11 +316,14 @@ Java_com_m2049r_xmrwallet_model_WalletManager_recoveryWalletJ(JNIEnv *env, jobje
|
||||
std::string(_password),
|
||||
std::string(_mnemonic),
|
||||
_networkType,
|
||||
(uint64_t) restoreHeight);
|
||||
(uint64_t) restoreHeight,
|
||||
1, // kdf_rounds
|
||||
std::string(_offset));
|
||||
|
||||
env->ReleaseStringUTFChars(path, _path);
|
||||
env->ReleaseStringUTFChars(password, _password);
|
||||
env->ReleaseStringUTFChars(mnemonic, _mnemonic);
|
||||
env->ReleaseStringUTFChars(offset, _offset);
|
||||
return reinterpret_cast<jlong>(wallet);
|
||||
}
|
||||
|
||||
@@ -533,7 +537,7 @@ Java_com_m2049r_xmrwallet_model_WalletManager_resolveOpenAlias(JNIEnv *env, jobj
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_m2049r_xmrwallet_model_WalletManager_setProxy(JNIEnv *env, jobject instance,
|
||||
jstring address) {
|
||||
jstring address) {
|
||||
const char *_address = env->GetStringUTFChars(address, nullptr);
|
||||
bool rc =
|
||||
Bitmonero::WalletManagerFactory::getWalletManager()->setProxy(std::string(_address));
|
||||
@@ -570,9 +574,12 @@ Java_com_m2049r_xmrwallet_model_WalletManager_closeJ(JNIEnv *env, jobject instan
|
||||
/**********************************/
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_com_m2049r_xmrwallet_model_Wallet_getSeed(JNIEnv *env, jobject instance) {
|
||||
Java_com_m2049r_xmrwallet_model_Wallet_getSeed(JNIEnv *env, jobject instance, jstring seedOffset) {
|
||||
const char *_seedOffset = env->GetStringUTFChars(seedOffset, nullptr);
|
||||
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
|
||||
return env->NewStringUTF(wallet->seed().c_str());
|
||||
jstring seed = env->NewStringUTF(wallet->seed(std::string(_seedOffset)).c_str());
|
||||
env->ReleaseStringUTFChars(seedOffset, _seedOffset);
|
||||
return seed;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
@@ -740,7 +747,7 @@ Java_com_m2049r_xmrwallet_model_Wallet_getConnectionStatusJ(JNIEnv *env, jobject
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_m2049r_xmrwallet_model_Wallet_setProxy(JNIEnv *env, jobject instance,
|
||||
jstring address) {
|
||||
jstring address) {
|
||||
const char *_address = env->GetStringUTFChars(address, nullptr);
|
||||
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
|
||||
bool rc = wallet->setProxy(std::string(_address));
|
||||
@@ -1262,7 +1269,7 @@ jobject newTransactionInfo(JNIEnv *env, Bitmonero::TransactionInfo *info) {
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
jobject cpp2java(JNIEnv *env, const std::vector<Bitmonero::TransactionInfo *>& vector) {
|
||||
jobject cpp2java(JNIEnv *env, const std::vector<Bitmonero::TransactionInfo *> &vector) {
|
||||
|
||||
jmethodID java_util_ArrayList_ = env->GetMethodID(class_ArrayList, "<init>", "(I)V");
|
||||
jmethodID java_util_ArrayList_add = env->GetMethodID(class_ArrayList, "add",
|
||||
|
@@ -78,7 +78,7 @@ public class BTChipTransportAndroidHID implements BTChipTransport {
|
||||
}
|
||||
|
||||
private static final int VID = 0x2C97;
|
||||
private static final int[] PID_HIDS = {0x0001, 0x0004};
|
||||
private static final int[] PID_HIDS = {0x0001, 0x0004, 0x0005};
|
||||
|
||||
private UsbDeviceConnection connection;
|
||||
private UsbInterface dongleInterface;
|
||||
|
@@ -27,6 +27,7 @@ import android.nfc.NfcAdapter;
|
||||
import android.nfc.Tag;
|
||||
import android.nfc.tech.Ndef;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
@@ -180,7 +181,7 @@ public class BaseActivity extends SecureActivity
|
||||
return;
|
||||
nfcPendingIntent = PendingIntent.getActivity(this, 0,
|
||||
new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
|
||||
0);
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_IMMUTABLE : 0);
|
||||
}
|
||||
|
||||
private void processNfcIntent(Intent intent) {
|
||||
|
@@ -36,7 +36,6 @@ import android.view.WindowManager;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
@@ -58,7 +57,6 @@ import com.m2049r.xmrwallet.widget.Toolbar;
|
||||
import java.io.File;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Objects;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
@@ -81,6 +79,9 @@ public class GenerateFragment extends Fragment {
|
||||
private TextInputLayout etWalletRestoreHeight;
|
||||
private Button bGenerate;
|
||||
|
||||
private Button bSeedOffset;
|
||||
private TextInputLayout etSeedOffset;
|
||||
|
||||
private String type = null;
|
||||
|
||||
private void clearErrorOnTextEntry(final TextInputLayout textInputLayout) {
|
||||
@@ -118,6 +119,8 @@ public class GenerateFragment extends Fragment {
|
||||
etWalletSpendKey = view.findViewById(R.id.etWalletSpendKey);
|
||||
etWalletRestoreHeight = view.findViewById(R.id.etWalletRestoreHeight);
|
||||
bGenerate = view.findViewById(R.id.bGenerate);
|
||||
bSeedOffset = view.findViewById(R.id.bSeedOffset);
|
||||
etSeedOffset = view.findViewById(R.id.etSeedOffset);
|
||||
|
||||
etWalletAddress.getEditText().setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||
etWalletViewKey.getEditText().setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||
@@ -222,6 +225,8 @@ public class GenerateFragment extends Fragment {
|
||||
}
|
||||
return false;
|
||||
});
|
||||
bSeedOffset.setVisibility(View.VISIBLE);
|
||||
bSeedOffset.setOnClickListener(v -> toggleSeedOffset());
|
||||
break;
|
||||
case TYPE_KEY:
|
||||
case TYPE_VIEWONLY:
|
||||
@@ -288,6 +293,18 @@ public class GenerateFragment extends Fragment {
|
||||
return view;
|
||||
}
|
||||
|
||||
void toggleSeedOffset() {
|
||||
if (etSeedOffset.getVisibility() == View.VISIBLE) {
|
||||
etSeedOffset.getEditText().getText().clear();
|
||||
etSeedOffset.setVisibility(View.GONE);
|
||||
bSeedOffset.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_keyboard_arrow_down, 0, 0, 0);
|
||||
} else {
|
||||
etSeedOffset.setVisibility(View.VISIBLE);
|
||||
bSeedOffset.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_keyboard_arrow_up, 0, 0, 0);
|
||||
etSeedOffset.requestFocusFromTouch();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkName() {
|
||||
String name = etWalletName.getEditText().getText().toString();
|
||||
boolean ok = true;
|
||||
@@ -422,12 +439,13 @@ public class GenerateFragment extends Fragment {
|
||||
break;
|
||||
case TYPE_SEED:
|
||||
if (!checkMnemonic()) return;
|
||||
String seed = etWalletMnemonic.getEditText().getText().toString();
|
||||
final String seed = etWalletMnemonic.getEditText().getText().toString();
|
||||
bGenerate.setEnabled(false);
|
||||
if (fingerprintAuthAllowed) {
|
||||
KeyStoreHelper.saveWalletUserPass(requireActivity(), name, password);
|
||||
}
|
||||
activityCallback.onGenerate(name, crazyPass, seed, height);
|
||||
final String offset = etSeedOffset.getEditText().getText().toString();
|
||||
activityCallback.onGenerate(name, crazyPass, seed, offset, height);
|
||||
break;
|
||||
case TYPE_LEDGER:
|
||||
bGenerate.setEnabled(false);
|
||||
@@ -491,7 +509,7 @@ public class GenerateFragment extends Fragment {
|
||||
public interface Listener {
|
||||
void onGenerate(String name, String password);
|
||||
|
||||
void onGenerate(String name, String password, String seed, long height);
|
||||
void onGenerate(String name, String password, String seed, String offset, long height);
|
||||
|
||||
void onGenerate(String name, String password, String address, String viewKey, String spendKey, long height);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -26,6 +26,7 @@ import android.content.SharedPreferences;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbManager;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -39,7 +40,6 @@ import android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
@@ -49,24 +49,19 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.m2049r.xmrwallet.data.DefaultNodes;
|
||||
import com.m2049r.xmrwallet.data.Node;
|
||||
import com.m2049r.xmrwallet.data.NodeInfo;
|
||||
import com.m2049r.xmrwallet.dialog.AboutFragment;
|
||||
import com.m2049r.xmrwallet.dialog.CreditsFragment;
|
||||
import com.m2049r.xmrwallet.dialog.HelpFragment;
|
||||
import com.m2049r.xmrwallet.dialog.PrivacyFragment;
|
||||
import com.m2049r.xmrwallet.ledger.Ledger;
|
||||
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
|
||||
import com.m2049r.xmrwallet.model.NetworkType;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.model.WalletManager;
|
||||
import com.m2049r.xmrwallet.service.WalletService;
|
||||
import com.m2049r.xmrwallet.util.DayNightMode;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.util.KeyStoreHelper;
|
||||
import com.m2049r.xmrwallet.util.LegacyStorageHelper;
|
||||
import com.m2049r.xmrwallet.util.LocaleHelper;
|
||||
import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor;
|
||||
import com.m2049r.xmrwallet.util.NetCipherHelper;
|
||||
import com.m2049r.xmrwallet.util.NightmodeHelper;
|
||||
import com.m2049r.xmrwallet.util.ThemeHelper;
|
||||
import com.m2049r.xmrwallet.util.ZipBackup;
|
||||
import com.m2049r.xmrwallet.util.ZipRestore;
|
||||
@@ -77,12 +72,8 @@ import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -90,7 +81,8 @@ import timber.log.Timber;
|
||||
|
||||
public class LoginActivity extends BaseActivity
|
||||
implements LoginFragment.Listener, GenerateFragment.Listener,
|
||||
GenerateReviewFragment.Listener, GenerateReviewFragment.AcceptListener, NodeFragment.Listener {
|
||||
GenerateReviewFragment.Listener, GenerateReviewFragment.AcceptListener,
|
||||
NodeFragment.Listener, SettingsFragment.Listener {
|
||||
private static final String GENERATE_STACK = "gen";
|
||||
|
||||
private static final String NODES_PREFS_NAME = "nodes";
|
||||
@@ -293,6 +285,7 @@ public class LoginActivity extends BaseActivity
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
Timber.d("onCreate()");
|
||||
ThemeHelper.setPreferred(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_login);
|
||||
@@ -308,8 +301,8 @@ public class LoginActivity extends BaseActivity
|
||||
case Toolbar.BUTTON_CLOSE:
|
||||
finish();
|
||||
break;
|
||||
case Toolbar.BUTTON_CREDITS:
|
||||
CreditsFragment.display(getSupportFragmentManager());
|
||||
case Toolbar.BUTTON_SETTINGS:
|
||||
startSettingsFragment();
|
||||
break;
|
||||
case Toolbar.BUTTON_NONE:
|
||||
break;
|
||||
@@ -785,6 +778,11 @@ public class LoginActivity extends BaseActivity
|
||||
Timber.d("NodeFragment placed");
|
||||
}
|
||||
|
||||
void startSettingsFragment() {
|
||||
replaceFragment(new SettingsFragment(), null, null);
|
||||
Timber.d("SettingsFragment placed");
|
||||
}
|
||||
|
||||
void replaceFragment(Fragment newFragment, String stackName, Bundle extras) {
|
||||
if (extras != null) {
|
||||
newFragment.setArguments(extras);
|
||||
@@ -929,7 +927,8 @@ public class LoginActivity extends BaseActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGenerate(final String name, final String password, final String seed,
|
||||
public void onGenerate(final String name, final String password,
|
||||
final String seed, final String offset,
|
||||
final long restoreHeight) {
|
||||
createWallet(name, password,
|
||||
new WalletCreator() {
|
||||
@@ -941,7 +940,7 @@ public class LoginActivity extends BaseActivity
|
||||
@Override
|
||||
public boolean createWallet(File aFile, String password) {
|
||||
Wallet newWallet = WalletManager.getInstance()
|
||||
.recoveryWallet(aFile, password, seed, restoreHeight);
|
||||
.recoveryWallet(aFile, password, seed, offset, restoreHeight);
|
||||
return checkAndCloseWallet(newWallet);
|
||||
}
|
||||
});
|
||||
@@ -1088,61 +1087,6 @@ public class LoginActivity extends BaseActivity
|
||||
}
|
||||
}
|
||||
|
||||
public void onChangeLocale() {
|
||||
final ArrayList<Locale> availableLocales = LocaleHelper.getAvailableLocales(LoginActivity.this);
|
||||
Collections.sort(availableLocales, (locale1, locale2) -> {
|
||||
String localeString1 = LocaleHelper.getDisplayName(locale1, true);
|
||||
String localeString2 = LocaleHelper.getDisplayName(locale2, true);
|
||||
return localeString1.compareTo(localeString2);
|
||||
});
|
||||
|
||||
String[] localeDisplayNames = new String[1 + availableLocales.size()];
|
||||
localeDisplayNames[0] = getString(R.string.language_system_default);
|
||||
for (int i = 1; i < localeDisplayNames.length; i++) {
|
||||
localeDisplayNames[i] = LocaleHelper.getDisplayName(availableLocales.get(i - 1), true);
|
||||
}
|
||||
|
||||
int currentLocaleIndex = 0;
|
||||
String currentLocaleTag = LocaleHelper.getPreferredLanguageTag(LoginActivity.this);
|
||||
if (!currentLocaleTag.isEmpty()) {
|
||||
Locale currentLocale = Locale.forLanguageTag(currentLocaleTag);
|
||||
String currentLocaleName = LocaleHelper.getDisplayName(currentLocale, true);
|
||||
currentLocaleIndex = Arrays.asList(localeDisplayNames).indexOf(currentLocaleName);
|
||||
if (currentLocaleIndex < 0) currentLocaleIndex = 0;
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(LoginActivity.this);
|
||||
builder.setTitle(getString(R.string.menu_language));
|
||||
builder.setSingleChoiceItems(localeDisplayNames, currentLocaleIndex, (dialog, i) -> {
|
||||
dialog.dismiss();
|
||||
|
||||
LocaleHelper.setAndSaveLocale(this,
|
||||
(i == 0) ? "" : availableLocales.get(i - 1).toLanguageTag());
|
||||
startActivity(getIntent().addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
|
||||
public void onChangeTheme() {
|
||||
final DayNightMode currentDayNightSetting = DayNightMode.getValue(AppCompatDelegate.getDefaultNightMode());
|
||||
// selection will be empty if UNKNOWN
|
||||
|
||||
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(LoginActivity.this);
|
||||
builder.setTitle(getString(R.string.menu_daynight));
|
||||
|
||||
String[] modeNames = getResources().getStringArray(R.array.daynight_themes);
|
||||
|
||||
builder.setSingleChoiceItems(modeNames, currentDayNightSetting.ordinal(), (dialog, i) -> {
|
||||
dialog.dismiss();
|
||||
final DayNightMode mode = DayNightMode.values()[i];
|
||||
if (currentDayNightSetting != mode) {
|
||||
NightmodeHelper.setAndSavePreferredNightmode(LoginActivity.this, mode);
|
||||
LoginActivity.this.recreate();
|
||||
}
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
|
||||
@@ -1191,9 +1135,6 @@ public class LoginActivity extends BaseActivity
|
||||
} else if (id == R.id.action_details_changepw) {
|
||||
onWalletChangePassword();
|
||||
return true;
|
||||
} else if (id == R.id.action_license_info) {
|
||||
AboutFragment.display(getSupportFragmentManager());
|
||||
return true;
|
||||
} else if (id == R.id.action_help_list) {
|
||||
HelpFragment.display(getSupportFragmentManager(), R.string.help_list);
|
||||
return true;
|
||||
@@ -1207,18 +1148,6 @@ public class LoginActivity extends BaseActivity
|
||||
((NodeFragment) f).restoreDefaultNodes();
|
||||
}
|
||||
return true;
|
||||
} else if (id == R.id.action_privacy_policy) {
|
||||
PrivacyFragment.display(getSupportFragmentManager());
|
||||
return true;
|
||||
} else if (id == R.id.action_language) {
|
||||
onChangeLocale();
|
||||
return true;
|
||||
} else if (id == R.id.action_theme) {
|
||||
onChangeTheme();
|
||||
return true;
|
||||
} else if (id == R.id.action_restore) {
|
||||
onWalletRestore();
|
||||
return true;
|
||||
} else if (id == R.id.action_ledger_seed) {
|
||||
Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
|
||||
if (f instanceof GenerateFragment) {
|
||||
@@ -1332,7 +1261,8 @@ public class LoginActivity extends BaseActivity
|
||||
registerReceiver(usbPermissionReceiver, new IntentFilter(ACTION_USB_PERMISSION));
|
||||
usbManager.requestPermission(device,
|
||||
PendingIntent.getBroadcast(this, 0,
|
||||
new Intent(ACTION_USB_PERMISSION), 0));
|
||||
new Intent(ACTION_USB_PERMISSION),
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_IMMUTABLE : 0));
|
||||
}
|
||||
} else {
|
||||
Timber.d("no ledger device found");
|
||||
|
@@ -38,7 +38,6 @@ import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
@@ -53,6 +52,7 @@ import com.m2049r.xmrwallet.util.KeyStoreHelper;
|
||||
import com.m2049r.xmrwallet.util.NetCipherHelper;
|
||||
import com.m2049r.xmrwallet.util.NodePinger;
|
||||
import com.m2049r.xmrwallet.util.Notice;
|
||||
import com.m2049r.xmrwallet.util.ThemeHelper;
|
||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||
|
||||
import java.io.File;
|
||||
@@ -143,7 +143,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||
super.onResume();
|
||||
Timber.d("onResume() %s", activityCallback.getFavouriteNodes().size());
|
||||
activityCallback.setTitle(null);
|
||||
activityCallback.setToolbarButton(Toolbar.BUTTON_CREDITS);
|
||||
activityCallback.setToolbarButton(Toolbar.BUTTON_SETTINGS);
|
||||
activityCallback.showNet();
|
||||
showNetwork();
|
||||
//activityCallback.runOnNetCipher(this::pingSelectedNode);
|
||||
@@ -163,12 +163,14 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||
fabView = view.findViewById(R.id.fabView);
|
||||
fabKey = view.findViewById(R.id.fabKey);
|
||||
fabSeed = view.findViewById(R.id.fabSeed);
|
||||
fabImport = view.findViewById(R.id.fabImport);
|
||||
fabLedger = view.findViewById(R.id.fabLedger);
|
||||
|
||||
fabNewL = view.findViewById(R.id.fabNewL);
|
||||
fabViewL = view.findViewById(R.id.fabViewL);
|
||||
fabKeyL = view.findViewById(R.id.fabKeyL);
|
||||
fabSeedL = view.findViewById(R.id.fabSeedL);
|
||||
fabImportL = view.findViewById(R.id.fabImportL);
|
||||
fabLedgerL = view.findViewById(R.id.fabLedgerL);
|
||||
|
||||
fab_pulse = AnimationUtils.loadAnimation(getContext(), R.anim.fab_pulse);
|
||||
@@ -183,6 +185,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||
fabView.setOnClickListener(this);
|
||||
fabKey.setOnClickListener(this);
|
||||
fabSeed.setOnClickListener(this);
|
||||
fabImport.setOnClickListener(this);
|
||||
fabLedger.setOnClickListener(this);
|
||||
fabScreen.setOnClickListener(this);
|
||||
|
||||
@@ -294,9 +297,9 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||
}
|
||||
|
||||
private boolean isFabOpen = false;
|
||||
private FloatingActionButton fab, fabNew, fabView, fabKey, fabSeed, fabLedger;
|
||||
private FrameLayout fabScreen;
|
||||
private RelativeLayout fabNewL, fabViewL, fabKeyL, fabSeedL, fabLedgerL;
|
||||
private FloatingActionButton fab, fabNew, fabView, fabKey, fabSeed, fabImport, fabLedger;
|
||||
private RelativeLayout fabScreen;
|
||||
private RelativeLayout fabNewL, fabViewL, fabKeyL, fabSeedL, fabImportL, fabLedgerL;
|
||||
private Animation fab_open, fab_close, rotate_forward, rotate_backward, fab_open_screen, fab_close_screen;
|
||||
private Animation fab_pulse;
|
||||
|
||||
@@ -321,6 +324,8 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||
fabKey.setClickable(false);
|
||||
fabSeedL.startAnimation(fab_close);
|
||||
fabSeed.setClickable(false);
|
||||
fabImportL.startAnimation(fab_close);
|
||||
fabImport.setClickable(false);
|
||||
}
|
||||
isFabOpen = false;
|
||||
} else { // open the fab
|
||||
@@ -333,6 +338,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||
fabViewL.setVisibility(View.GONE);
|
||||
fabKeyL.setVisibility(View.GONE);
|
||||
fabSeedL.setVisibility(View.GONE);
|
||||
fabImportL.setVisibility(View.GONE);
|
||||
|
||||
fabLedgerL.startAnimation(fab_open);
|
||||
fabLedger.setClickable(true);
|
||||
@@ -342,6 +348,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||
fabViewL.setVisibility(View.VISIBLE);
|
||||
fabKeyL.setVisibility(View.VISIBLE);
|
||||
fabSeedL.setVisibility(View.VISIBLE);
|
||||
fabImportL.setVisibility(View.VISIBLE);
|
||||
|
||||
fabNewL.startAnimation(fab_open);
|
||||
fabNew.setClickable(true);
|
||||
@@ -351,6 +358,8 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||
fabKey.setClickable(true);
|
||||
fabSeedL.startAnimation(fab_open);
|
||||
fabSeed.setClickable(true);
|
||||
fabImportL.startAnimation(fab_open);
|
||||
fabImport.setClickable(true);
|
||||
}
|
||||
isFabOpen = true;
|
||||
}
|
||||
@@ -375,6 +384,9 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||
} else if (id == R.id.fabSeed) {
|
||||
animateFAB();
|
||||
activityCallback.onAddWallet(GenerateFragment.TYPE_SEED);
|
||||
} else if (id == R.id.fabImport) {
|
||||
animateFAB();
|
||||
activityCallback.onWalletRestore();
|
||||
} else if (id == R.id.fabLedger) {
|
||||
Timber.d("FAB_LEDGER");
|
||||
animateFAB();
|
||||
@@ -403,8 +415,8 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||
private void setSubtext(String status) {
|
||||
final Context ctx = 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),
|
||||
Integer.toHexString(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor) & 0xFFFFFF),
|
||||
Integer.toHexString(ThemeHelper.getThemedColor(ctx, android.R.attr.colorBackground) & 0xFFFFFF),
|
||||
status, ""));
|
||||
tvNodeInfo.setText(text);
|
||||
}
|
||||
|
@@ -415,7 +415,14 @@ public class NodeFragment extends Fragment
|
||||
}
|
||||
etNodeHost.setError(null);
|
||||
nodeInfo.setRpcPort(port);
|
||||
nodeInfo.setName(etNodeName.getEditText().getText().toString().trim());
|
||||
// setName() may trigger reverse DNS
|
||||
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.setPassword(etNodePass.getEditText().getText().toString()); // no trim for pw
|
||||
return true;
|
||||
|
@@ -46,10 +46,7 @@ import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.ShareActionProvider;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.content.FileProvider;
|
||||
import androidx.core.view.MenuItemCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
@@ -89,6 +86,7 @@ public class ReceiveFragment extends Fragment {
|
||||
private ImageView ivQrCodeFull;
|
||||
private EditText etDummy;
|
||||
private ImageButton bCopyAddress;
|
||||
private MenuItem shareItem;
|
||||
|
||||
private Wallet wallet = null;
|
||||
private boolean isMyWallet = false;
|
||||
@@ -128,6 +126,7 @@ public class ReceiveFragment extends Fragment {
|
||||
evAmount.setOnNewAmountListener(xmr -> {
|
||||
Timber.d("new amount = %s", xmr);
|
||||
generateQr();
|
||||
if (shareRequested && (xmr != null)) share();
|
||||
});
|
||||
|
||||
evAmount.setOnFailedExchangeListener(() -> {
|
||||
@@ -211,39 +210,38 @@ public class ReceiveFragment extends Fragment {
|
||||
setSharedElementEnterTransition(transform);
|
||||
}
|
||||
|
||||
private ShareActionProvider shareActionProvider;
|
||||
private boolean shareRequested = false;
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(@NonNull Menu menu, final MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.receive_menu, menu);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
|
||||
// Locate MenuItem with ShareActionProvider
|
||||
MenuItem item = menu.findItem(R.id.menu_item_share);
|
||||
|
||||
// Fetch and store ShareActionProvider
|
||||
shareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(item);
|
||||
|
||||
shareActionProvider.setOnShareTargetSelectedListener(new ShareActionProvider.OnShareTargetSelectedListener() {
|
||||
@Override
|
||||
public boolean onShareTargetSelected(ShareActionProvider shareActionProvider, Intent intent) {
|
||||
saveQrCode(); // save it only if we need it
|
||||
return false;
|
||||
shareItem = menu.findItem(R.id.menu_item_share);
|
||||
shareItem.setOnMenuItemClickListener(item -> {
|
||||
if (shareRequested) return true;
|
||||
shareRequested = true;
|
||||
if (!qrValid) {
|
||||
evAmount.doExchange();
|
||||
} else {
|
||||
share();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void setShareIntent() {
|
||||
if (shareActionProvider != null) {
|
||||
if (qrValid) {
|
||||
shareActionProvider.setShareIntent(getShareIntent());
|
||||
} else {
|
||||
shareActionProvider.setShareIntent(null);
|
||||
}
|
||||
private void share() {
|
||||
shareRequested = false;
|
||||
if (saveQrCode()) {
|
||||
final Intent sendIntent = getSendIntent();
|
||||
if (sendIntent != null)
|
||||
startActivity(Intent.createChooser(sendIntent, null));
|
||||
} else {
|
||||
Toast.makeText(getActivity(), getString(R.string.message_qr_failed), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void saveQrCode() {
|
||||
private boolean saveQrCode() {
|
||||
if (!qrValid) throw new IllegalStateException("trying to save null qr code!");
|
||||
|
||||
File cachePath = new File(getActivity().getCacheDir(), "images");
|
||||
@@ -255,33 +253,35 @@ public class ReceiveFragment extends Fragment {
|
||||
Bitmap qrBitmap = ((BitmapDrawable) ivQrCode.getDrawable()).getBitmap();
|
||||
qrBitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
|
||||
stream.close();
|
||||
return true;
|
||||
} catch (IOException ex) {
|
||||
Timber.e(ex);
|
||||
// make sure we don't share an old qr code
|
||||
if (!png.delete()) throw new IllegalStateException("cannot delete old qr code");
|
||||
// if we manage to delete it, the URI points to nothing and the user gets a toast with the error
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Intent getShareIntent() {
|
||||
File imagePath = new File(getActivity().getCacheDir(), "images");
|
||||
private Intent getSendIntent() {
|
||||
File imagePath = new File(requireActivity().getCacheDir(), "images");
|
||||
File png = new File(imagePath, "QR.png");
|
||||
Uri contentUri = FileProvider.getUriForFile(getActivity(),
|
||||
BuildConfig.APPLICATION_ID + ".fileprovider", png);
|
||||
Uri contentUri = FileProvider.getUriForFile(requireActivity(), BuildConfig.APPLICATION_ID + ".fileprovider", png);
|
||||
if (contentUri != null) {
|
||||
Intent shareIntent = new Intent();
|
||||
shareIntent.setAction(Intent.ACTION_SEND);
|
||||
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // temp permission for receiving app to read this file
|
||||
shareIntent.setDataAndType(contentUri, getActivity().getContentResolver().getType(contentUri));
|
||||
shareIntent.setTypeAndNormalize("image/png");
|
||||
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
|
||||
shareIntent.putExtra(Intent.EXTRA_TEXT, bcData.getUriString());
|
||||
if (bcData != null)
|
||||
shareIntent.putExtra(Intent.EXTRA_TEXT, bcData.getUriString());
|
||||
return shareIntent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void copyAddress() {
|
||||
Helper.clipBoardCopy(Objects.requireNonNull(getActivity()), getString(R.string.label_copy_address), subaddress.getAddress());
|
||||
Helper.clipBoardCopy(requireActivity(), getString(R.string.label_copy_address), subaddress.getAddress());
|
||||
Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@@ -291,7 +291,6 @@ public class ReceiveFragment extends Fragment {
|
||||
if (qrValid) {
|
||||
ivQrCode.setImageBitmap(null);
|
||||
qrValid = false;
|
||||
setShareIntent();
|
||||
if (isLoaded)
|
||||
tvQrCode.setVisibility(View.VISIBLE);
|
||||
}
|
||||
@@ -300,7 +299,6 @@ public class ReceiveFragment extends Fragment {
|
||||
void setQR(Bitmap qr) {
|
||||
ivQrCode.setImageBitmap(qr);
|
||||
qrValid = true;
|
||||
setShareIntent();
|
||||
tvQrCode.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@@ -462,8 +460,8 @@ public class ReceiveFragment extends Fragment {
|
||||
subaddress = newSubaddress;
|
||||
final Context context = getContext();
|
||||
Spanned label = Html.fromHtml(context.getString(R.string.receive_subaddress,
|
||||
Integer.toHexString(ContextCompat.getColor(context, R.color.monerujoGreen) & 0xFFFFFF),
|
||||
Integer.toHexString(ContextCompat.getColor(context, R.color.monerujoBackground) & 0xFFFFFF),
|
||||
Integer.toHexString(ThemeHelper.getThemedColor(context, R.attr.positiveColor) & 0xFFFFFF),
|
||||
Integer.toHexString(ThemeHelper.getThemedColor(context, android.R.attr.colorBackground) & 0xFFFFFF),
|
||||
subaddress.getDisplayLabel(), subaddress.getAddress()));
|
||||
tvAddress.setText(label);
|
||||
generateQr();
|
||||
|
@@ -66,11 +66,7 @@ public abstract class SecureActivity extends AppCompatActivity {
|
||||
|
||||
Locale locale = LocaleHelper.getPreferredLocale(this);
|
||||
if (locale != null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
config.setLocale(locale);
|
||||
} else {
|
||||
config.locale = locale;
|
||||
}
|
||||
config.setLocale(locale);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
125
app/src/main/java/com/m2049r/xmrwallet/SettingsFragment.java
Normal file
125
app/src/main/java/com/m2049r/xmrwallet/SettingsFragment.java
Normal file
@@ -0,0 +1,125 @@
|
||||
package com.m2049r.xmrwallet;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.m2049r.xmrwallet.dialog.AboutFragment;
|
||||
import com.m2049r.xmrwallet.dialog.CreditsFragment;
|
||||
import com.m2049r.xmrwallet.dialog.PrivacyFragment;
|
||||
import com.m2049r.xmrwallet.util.DayNightMode;
|
||||
import com.m2049r.xmrwallet.util.LocaleHelper;
|
||||
import com.m2049r.xmrwallet.util.NightmodeHelper;
|
||||
import com.m2049r.xmrwallet.util.ThemeHelper;
|
||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class SettingsFragment extends PreferenceFragmentCompat
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
setPreferencesFromResource(R.xml.root_preferences, rootKey);
|
||||
|
||||
findPreference(getString(R.string.about_info)).setOnPreferenceClickListener(preference -> {
|
||||
AboutFragment.display(getParentFragmentManager());
|
||||
return true;
|
||||
});
|
||||
findPreference(getString(R.string.privacy_info)).setOnPreferenceClickListener(preference -> {
|
||||
PrivacyFragment.display(getParentFragmentManager());
|
||||
return true;
|
||||
});
|
||||
findPreference(getString(R.string.credits_info)).setOnPreferenceClickListener(preference -> {
|
||||
CreditsFragment.display(getParentFragmentManager());
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if (key.equals(getString(R.string.preferred_locale))) {
|
||||
activity.recreate();
|
||||
} else if (key.equals(getString(R.string.preferred_nightmode))) {
|
||||
NightmodeHelper.setNightMode(DayNightMode.valueOf(sharedPreferences.getString(key, "AUTO")));
|
||||
} else if (key.equals(getString(R.string.preferred_theme))) {
|
||||
ThemeHelper.setTheme((Activity) activity, sharedPreferences.getString(key, "Classic"));
|
||||
activity.recreate();
|
||||
}
|
||||
}
|
||||
|
||||
private SettingsFragment.Listener activity;
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof SettingsFragment.Listener) {
|
||||
activity = (SettingsFragment.Listener) context;
|
||||
} else {
|
||||
throw new ClassCastException(context + " must implement Listener");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
Timber.d("onResume()");
|
||||
activity.setSubtitle(getString(R.string.menu_settings));
|
||||
activity.setToolbarButton(Toolbar.BUTTON_BACK);
|
||||
populateLanguages();
|
||||
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.registerOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.unregisterOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
public interface Listener {
|
||||
void setToolbarButton(int type);
|
||||
|
||||
void setSubtitle(String title);
|
||||
|
||||
void recreate();
|
||||
|
||||
void setTheme(@StyleRes final int resId);
|
||||
}
|
||||
|
||||
public void populateLanguages() {
|
||||
ListPreference language = findPreference(getString(R.string.preferred_locale));
|
||||
assert language != null;
|
||||
|
||||
final ArrayList<Locale> availableLocales = LocaleHelper.getAvailableLocales(requireContext());
|
||||
Collections.sort(availableLocales, (locale1, locale2) -> {
|
||||
String localeString1 = LocaleHelper.getDisplayName(locale1, true);
|
||||
String localeString2 = LocaleHelper.getDisplayName(locale2, true);
|
||||
return localeString1.compareTo(localeString2);
|
||||
});
|
||||
|
||||
String[] localeDisplayNames = new String[1 + availableLocales.size()];
|
||||
localeDisplayNames[0] = getString(R.string.language_system_default);
|
||||
for (int i = 1; i < localeDisplayNames.length; i++) {
|
||||
localeDisplayNames[i] = LocaleHelper.getDisplayName(availableLocales.get(i - 1), true);
|
||||
}
|
||||
language.setEntries(localeDisplayNames);
|
||||
|
||||
String[] languageTags = new String[1 + availableLocales.size()];
|
||||
languageTags[0] = "";
|
||||
for (int i = 1; i < languageTags.length; i++) {
|
||||
languageTags[i] = availableLocales.get(i - 1).toLanguageTag();
|
||||
}
|
||||
language.setEntryValues(languageTags);
|
||||
}
|
||||
}
|
@@ -30,7 +30,6 @@ 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;
|
||||
@@ -67,6 +66,8 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
|
||||
void setToolbarButton(int type);
|
||||
|
||||
void showSubaddress(View view, final int subaddressIndex);
|
||||
|
||||
void saveWallet();
|
||||
}
|
||||
|
||||
public interface ProgressListener {
|
||||
@@ -115,14 +116,6 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
|
||||
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) {
|
||||
@@ -152,11 +145,6 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
postponeEnterTransition();
|
||||
view.getViewTreeObserver().addOnPreDrawListener(() -> {
|
||||
startPostponedEnterTransition();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public void loadList() {
|
||||
@@ -217,7 +205,9 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
if (params.length != 0) return false;
|
||||
wallet.getNewSubaddress();
|
||||
wallet.store();
|
||||
if (activityCallback != null) {
|
||||
activityCallback.saveWallet();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -29,15 +29,15 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.transition.Transition;
|
||||
import androidx.transition.TransitionInflater;
|
||||
|
||||
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;
|
||||
@@ -102,10 +102,8 @@ public class SubaddressInfoFragment extends Fragment
|
||||
@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));
|
||||
Transition transform = TransitionInflater.from(requireContext())
|
||||
.inflateTransition(R.transition.details);
|
||||
setSharedElementEnterTransition(transform);
|
||||
}
|
||||
|
||||
|
@@ -35,11 +35,10 @@ import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.transition.Transition;
|
||||
import androidx.transition.TransitionInflater;
|
||||
|
||||
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.model.TransactionInfo;
|
||||
@@ -97,13 +96,6 @@ public class TxFragment extends Fragment {
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
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);
|
||||
tvTxXmrToKey = view.findViewById(R.id.tvTxXmrToKey);
|
||||
tvDestinationBtc = view.findViewById(R.id.tvDestinationBtc);
|
||||
@@ -220,8 +212,8 @@ public class TxFragment extends Fragment {
|
||||
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),
|
||||
Integer.toHexString(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor) & 0xFFFFFF),
|
||||
Integer.toHexString(ThemeHelper.getThemedColor(ctx, android.R.attr.colorBackground) & 0xFFFFFF),
|
||||
subaddress.getDisplayLabel()));
|
||||
tvAccount.setText(label);
|
||||
tvAccount.setOnClickListener(v -> activityCallback.showSubaddress(v, info.addressIndex));
|
||||
@@ -266,13 +258,13 @@ public class TxFragment extends Fragment {
|
||||
if (info.isFailed) {
|
||||
tvTxAmount.setText(getString(R.string.tx_list_amount_failed, Wallet.getDisplayAmount(info.amount)));
|
||||
tvTxFee.setText(getString(R.string.tx_list_failed_text));
|
||||
setTxColour(ContextCompat.getColor(getContext(), R.color.tx_failed));
|
||||
setTxColour(ThemeHelper.getThemedColor(getContext(), R.attr.neutralColor));
|
||||
} else if (info.isPending) {
|
||||
setTxColour(ContextCompat.getColor(getContext(), R.color.tx_pending));
|
||||
setTxColour(ThemeHelper.getThemedColor(getContext(), R.attr.neutralColor));
|
||||
} else if (info.direction == TransactionInfo.Direction.Direction_In) {
|
||||
setTxColour(ContextCompat.getColor(getContext(), R.color.tx_plus));
|
||||
setTxColour(ThemeHelper.getThemedColor(getContext(), R.attr.positiveColor));
|
||||
} else {
|
||||
setTxColour(ContextCompat.getColor(getContext(), R.color.tx_minus));
|
||||
setTxColour(ThemeHelper.getThemedColor(getContext(), R.attr.negativeColor));
|
||||
}
|
||||
Set<String> destinations = new HashSet<>();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@@ -348,10 +340,8 @@ public class TxFragment extends Fragment {
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
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));
|
||||
Transition transform = TransitionInflater.from(requireContext())
|
||||
.inflateTransition(R.transition.details);
|
||||
setSharedElementEnterTransition(transform);
|
||||
}
|
||||
|
||||
|
@@ -356,6 +356,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
Timber.d("onCreate()");
|
||||
ThemeHelper.setPreferred(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
if (savedInstanceState != null) {
|
||||
// activity restarted
|
||||
@@ -385,7 +386,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
||||
case Toolbar.BUTTON_CLOSE:
|
||||
finish();
|
||||
break;
|
||||
case Toolbar.BUTTON_CREDITS:
|
||||
case Toolbar.BUTTON_SETTINGS:
|
||||
Toast.makeText(WalletActivity.this, getString(R.string.label_credits), Toast.LENGTH_SHORT).show();
|
||||
case Toolbar.BUTTON_NONE:
|
||||
default:
|
||||
@@ -505,6 +506,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
||||
Timber.d("onResume()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveWallet() {
|
||||
if (mIsBound) { // no point in talking to unbound service
|
||||
Intent intent = new Intent(getApplicationContext(), WalletService.class);
|
||||
@@ -537,7 +539,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
||||
|
||||
@Override
|
||||
public void onSendRequest(View view) {
|
||||
replaceFragmentWithTransition(view, SendFragment.newInstance(uri), null, null);
|
||||
replaceFragment(SendFragment.newInstance(uri), null, null);
|
||||
uri = null; // only use uri once
|
||||
}
|
||||
|
||||
@@ -609,9 +611,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
||||
@Override
|
||||
public void onWalletStored(final boolean success) {
|
||||
runOnUiThread(() -> {
|
||||
if (success) {
|
||||
Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_unloaded), Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
if (!success) {
|
||||
Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_unload_failed), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
@@ -800,10 +800,6 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
||||
int transition;
|
||||
if (newFragment instanceof TxFragment)
|
||||
transition = R.string.tx_details_transition_name;
|
||||
else if (newFragment instanceof ReceiveFragment)
|
||||
transition = R.string.receive_transition_name;
|
||||
else if (newFragment instanceof SendFragment)
|
||||
transition = R.string.send_transition_name;
|
||||
else if (newFragment instanceof SubaddressInfoFragment)
|
||||
transition = R.string.subaddress_info_transition_name;
|
||||
else
|
||||
@@ -941,6 +937,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
Timber.d("onRequestPermissionsResult()");
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (requestCode == Helper.PERMISSIONS_REQUEST_CAMERA) { // If request is cancelled, the result arrays are empty.
|
||||
if (grantResults.length > 0
|
||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
@@ -959,7 +956,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
||||
Bundle b = new Bundle();
|
||||
b.putString("address", address);
|
||||
b.putString("name", getWalletName());
|
||||
replaceFragmentWithTransition(view, new ReceiveFragment(), null, b);
|
||||
replaceFragment(new ReceiveFragment(), null, b);
|
||||
Timber.d("ReceiveFragment placed");
|
||||
}
|
||||
|
||||
|
@@ -43,7 +43,6 @@ import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.github.brnunes.swipeablerecyclerview.SwipeableRecyclerViewTouchListener;
|
||||
import com.google.android.material.transition.MaterialElevationScale;
|
||||
import com.m2049r.xmrwallet.layout.TransactionInfoAdapter;
|
||||
import com.m2049r.xmrwallet.model.TransactionInfo;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
@@ -52,6 +51,7 @@ import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
|
||||
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.util.ServiceHelper;
|
||||
import com.m2049r.xmrwallet.util.ThemeHelper;
|
||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
@@ -111,7 +111,8 @@ public class WalletFragment extends Fragment
|
||||
llBalance = view.findViewById(R.id.llBalance);
|
||||
flExchange = view.findViewById(R.id.flExchange);
|
||||
((ProgressBar) view.findViewById(R.id.pbExchange)).getIndeterminateDrawable().
|
||||
setColorFilter(getResources().getColor(R.color.progress_circle),
|
||||
setColorFilter(
|
||||
ThemeHelper.getThemedColor(getContext(), R.attr.colorPrimaryVariant),
|
||||
android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||
|
||||
tvProgress = view.findViewById(R.id.tvProgress);
|
||||
@@ -202,11 +203,6 @@ public class WalletFragment extends Fragment
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
postponeEnterTransition();
|
||||
view.getViewTreeObserver().addOnPreDrawListener(() -> {
|
||||
startPostponedEnterTransition();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void showBalance(String balance) {
|
||||
@@ -331,19 +327,9 @@ public class WalletFragment extends Fragment
|
||||
// Callbacks from TransactionInfoAdapter
|
||||
@Override
|
||||
public void onInteraction(final View view, final TransactionInfo infoItem) {
|
||||
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);
|
||||
|
||||
activityCallback.onTxDetailsRequest(view, infoItem);
|
||||
}
|
||||
|
||||
// called from activity
|
||||
|
||||
|
||||
// if account index has changed scroll to top?
|
||||
private int accountIndex = 0;
|
||||
|
||||
@@ -536,8 +522,6 @@ public class WalletFragment extends Fragment
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
setExitTransition(null);
|
||||
setReenterTransition(null);
|
||||
Timber.d("onResume()");
|
||||
activityCallback.setTitle(walletTitle, walletSubtitle);
|
||||
activityCallback.setToolbarButton(Toolbar.BUTTON_NONE);
|
||||
|
@@ -22,6 +22,7 @@ import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
||||
import com.m2049r.xmrwallet.model.NetworkType;
|
||||
import com.m2049r.xmrwallet.util.LocaleHelper;
|
||||
@@ -35,7 +36,7 @@ public class XmrWalletApplication extends Application {
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
FragmentManager.enableNewStateManager(false);
|
||||
if (BuildConfig.DEBUG) {
|
||||
Timber.plant(new Timber.DebugTree());
|
||||
}
|
||||
|
@@ -267,14 +267,14 @@ public class NodeInfo extends Node {
|
||||
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),
|
||||
Integer.toHexString(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor) & 0xFFFFFF),
|
||||
Integer.toHexString(ThemeHelper.getThemedColor(ctx, android.R.attr.colorBackground) & 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));
|
||||
view.setTextColor(ThemeHelper.getThemedColor(ctx, android.R.attr.textColorSecondary));
|
||||
}
|
||||
|
||||
public void showInfo(TextView view) {
|
||||
|
@@ -40,6 +40,7 @@ import com.m2049r.xmrwallet.service.shift.sideshift.api.SideShiftApi;
|
||||
import com.m2049r.xmrwallet.service.shift.sideshift.network.SideShiftApiImpl;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.util.ServiceHelper;
|
||||
import com.m2049r.xmrwallet.util.ThemeHelper;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Locale;
|
||||
@@ -212,19 +213,27 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
|
||||
if (status.isError()) {
|
||||
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_error, status.toString()));
|
||||
statusResource = R.drawable.ic_error_red_24dp;
|
||||
pbXmrto.getIndeterminateDrawable().setColorFilter(0xff8b0000, android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||
pbXmrto.getIndeterminateDrawable().setColorFilter(
|
||||
ThemeHelper.getThemedColor(getContext(), android.R.attr.colorError),
|
||||
android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||
} else if (status.isSent() || status.isPaid()) {
|
||||
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_sent, btcData.getBtcSymbol()));
|
||||
statusResource = R.drawable.ic_success_green_24dp;
|
||||
pbXmrto.getIndeterminateDrawable().setColorFilter(0xFF417505, android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||
statusResource = R.drawable.ic_success;
|
||||
pbXmrto.getIndeterminateDrawable().setColorFilter(
|
||||
ThemeHelper.getThemedColor(getContext(), R.attr.positiveColor),
|
||||
android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||
} else if (status.isWaiting()) {
|
||||
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_unpaid));
|
||||
statusResource = R.drawable.ic_pending_orange_24dp;
|
||||
pbXmrto.getIndeterminateDrawable().setColorFilter(0xFFFF6105, android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||
statusResource = R.drawable.ic_pending;
|
||||
pbXmrto.getIndeterminateDrawable().setColorFilter(
|
||||
ThemeHelper.getThemedColor(getContext(), R.attr.neutralColor),
|
||||
android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||
} else if (status.isPending()) {
|
||||
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_paid));
|
||||
statusResource = R.drawable.ic_pending_orange_24dp;
|
||||
pbXmrto.getIndeterminateDrawable().setColorFilter(0xFFFF6105, android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||
statusResource = R.drawable.ic_pending;
|
||||
pbXmrto.getIndeterminateDrawable().setColorFilter(
|
||||
ThemeHelper.getThemedColor(getContext(), R.attr.neutralColor),
|
||||
android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||
} else {
|
||||
throw new IllegalStateException("status is broken: " + status.toString());
|
||||
}
|
||||
|
@@ -55,7 +55,6 @@ import com.m2049r.xmrwallet.widget.DotBar;
|
||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
@@ -202,14 +201,14 @@ public class SendFragment extends Fragment
|
||||
CharSequence nextLabel = pagerAdapter.getPageTitle(position + 1);
|
||||
bNext.setText(nextLabel);
|
||||
if (nextLabel != null) {
|
||||
bNext.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_navigate_next_white_24dp, 0);
|
||||
bNext.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_navigate_next, 0);
|
||||
} else {
|
||||
bNext.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
|
||||
}
|
||||
CharSequence prevLabel = pagerAdapter.getPageTitle(position - 1);
|
||||
bPrev.setText(prevLabel);
|
||||
if (prevLabel != null) {
|
||||
bPrev.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_navigate_prev_white_24dp, 0, 0, 0);
|
||||
bPrev.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_navigate_prev, 0, 0, 0);
|
||||
} else {
|
||||
bPrev.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
|
||||
}
|
||||
|
@@ -23,7 +23,6 @@ import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
@@ -230,21 +229,21 @@ public class NodeInfoAdapter extends RecyclerView.Adapter<NodeInfoAdapter.ViewHo
|
||||
|
||||
static public int getPingIcon(NodeInfo nodeInfo) {
|
||||
if (nodeInfo.isUnauthorized()) {
|
||||
return R.drawable.ic_wifi_lock_black_24dp;
|
||||
return R.drawable.ic_wifi_lock;
|
||||
}
|
||||
if (nodeInfo.isValid()) {
|
||||
final double ping = nodeInfo.getResponseTime();
|
||||
if (ping < NodeInfo.PING_GOOD) {
|
||||
return R.drawable.ic_signal_wifi_4_bar_24dp;
|
||||
return R.drawable.ic_wifi_4_bar;
|
||||
} else if (ping < NodeInfo.PING_MEDIUM) {
|
||||
return R.drawable.ic_signal_wifi_3_bar_24dp;
|
||||
return R.drawable.ic_wifi_3_bar;
|
||||
} else if (ping < NodeInfo.PING_BAD) {
|
||||
return R.drawable.ic_signal_wifi_2_bar_24dp;
|
||||
return R.drawable.ic_wifi_2_bar;
|
||||
} else {
|
||||
return R.drawable.ic_signal_wifi_1_bar_24dp;
|
||||
return R.drawable.ic_wifi_1_bar;
|
||||
}
|
||||
} else {
|
||||
return R.drawable.ic_signal_wifi_off_24dp;
|
||||
return R.drawable.ic_wifi_off;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -36,6 +36,7 @@ import com.m2049r.xmrwallet.data.Crypto;
|
||||
import com.m2049r.xmrwallet.data.UserNotes;
|
||||
import com.m2049r.xmrwallet.model.TransactionInfo;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.util.ThemeHelper;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
@@ -67,10 +68,10 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
||||
|
||||
public TransactionInfoAdapter(Context context, OnInteractionListener listener) {
|
||||
this.context = context;
|
||||
inboundColour = ContextCompat.getColor(context, R.color.tx_plus);
|
||||
outboundColour = ContextCompat.getColor(context, R.color.tx_minus);
|
||||
pendingColour = ContextCompat.getColor(context, R.color.tx_pending);
|
||||
failedColour = ContextCompat.getColor(context, R.color.tx_failed);
|
||||
inboundColour = ThemeHelper.getThemedColor(context, R.attr.positiveColor);
|
||||
outboundColour = ThemeHelper.getThemedColor(context, R.attr.negativeColor);
|
||||
pendingColour = ThemeHelper.getThemedColor(context, R.attr.neutralColor);
|
||||
failedColour = ThemeHelper.getThemedColor(context, R.attr.neutralColor);
|
||||
infoItems = new ArrayList<>();
|
||||
this.listener = listener;
|
||||
Calendar cal = Calendar.getInstance();
|
||||
@@ -253,8 +254,8 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
||||
tvPaymentId.setText(info);
|
||||
} else {
|
||||
Spanned label = Html.fromHtml(context.getString(R.string.tx_details_notes,
|
||||
Integer.toHexString(ContextCompat.getColor(context, R.color.monerujoGreen) & 0xFFFFFF),
|
||||
Integer.toHexString(ContextCompat.getColor(context, R.color.monerujoBackground) & 0xFFFFFF),
|
||||
Integer.toHexString(ThemeHelper.getThemedColor(context, R.attr.positiveColor) & 0xFFFFFF),
|
||||
Integer.toHexString(ThemeHelper.getThemedColor(context, android.R.attr.colorBackground) & 0xFFFFFF),
|
||||
tag, info.isEmpty() ? "" : (" " + info)));
|
||||
tvPaymentId.setText(label);
|
||||
}
|
||||
|
@@ -129,7 +129,7 @@ public class Wallet {
|
||||
ConnectionStatus_WrongVersion
|
||||
}
|
||||
|
||||
public native String getSeed();
|
||||
public native String getSeed(String offset);
|
||||
|
||||
public native String getSeedLanguage();
|
||||
|
||||
@@ -147,7 +147,7 @@ public class Wallet {
|
||||
|
||||
private native Status statusWithErrorString();
|
||||
|
||||
public native boolean setPassword(String password);
|
||||
public native synchronized boolean setPassword(String password);
|
||||
|
||||
public String getAddress() {
|
||||
return getAddress(accountIndex);
|
||||
@@ -203,12 +203,10 @@ public class Wallet {
|
||||
public native String getSecretSpendKey();
|
||||
|
||||
public boolean store() {
|
||||
final boolean ok = store("");
|
||||
Timber.d("stored");
|
||||
return ok;
|
||||
return store("");
|
||||
}
|
||||
|
||||
public native boolean store(String path);
|
||||
public native synchronized boolean store(String path);
|
||||
|
||||
public boolean close() {
|
||||
disposePendingTransaction();
|
||||
|
@@ -124,19 +124,19 @@ public class WalletManager {
|
||||
|
||||
private native long openWalletJ(String path, String password, int networkType);
|
||||
|
||||
public Wallet recoveryWallet(File aFile, String password, String mnemonic) {
|
||||
return recoveryWallet(aFile, password, mnemonic, 0);
|
||||
}
|
||||
|
||||
public Wallet recoveryWallet(File aFile, String password, String mnemonic, long restoreHeight) {
|
||||
long walletHandle = recoveryWalletJ(aFile.getAbsolutePath(), password, mnemonic,
|
||||
public Wallet recoveryWallet(File aFile, String password,
|
||||
String mnemonic, String offset,
|
||||
long restoreHeight) {
|
||||
long walletHandle = recoveryWalletJ(aFile.getAbsolutePath(), password,
|
||||
mnemonic, offset,
|
||||
getNetworkType().getValue(), restoreHeight);
|
||||
Wallet wallet = new Wallet(walletHandle);
|
||||
manageWallet(wallet);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
private native long recoveryWalletJ(String path, String password, String mnemonic,
|
||||
private native long recoveryWalletJ(String path, String password,
|
||||
String mnemonic, String offset,
|
||||
int networkType, long restoreHeight);
|
||||
|
||||
public Wallet createWalletWithKeys(File aFile, String password, String language, long restoreHeight,
|
||||
|
@@ -568,7 +568,8 @@ public class WalletService extends Service {
|
||||
|
||||
private void startNotfication() {
|
||||
Intent notificationIntent = new Intent(this, WalletActivity.class);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent,
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_IMMUTABLE : 0);
|
||||
|
||||
String channelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? createNotificationChannel() : "";
|
||||
Notification notification = new NotificationCompat.Builder(this, channelId)
|
||||
|
@@ -395,7 +395,7 @@ public class Helper {
|
||||
final TextView tvOpenPrompt = promptsView.findViewById(R.id.tvOpenPrompt);
|
||||
final Drawable icFingerprint = context.getDrawable(R.drawable.ic_fingerprint);
|
||||
final Drawable icError = context.getDrawable(R.drawable.ic_error_red_36dp);
|
||||
final Drawable icInfo = context.getDrawable(R.drawable.ic_info_green_36dp);
|
||||
final Drawable icInfo = context.getDrawable(R.drawable.ic_info_white_24dp);
|
||||
|
||||
final boolean fingerprintAuthCheck = FingerprintHelper.isFingerPassValid(context, wallet);
|
||||
|
||||
|
@@ -27,7 +27,6 @@ import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
public class LocaleHelper {
|
||||
private static final String PREFERRED_LOCALE_KEY = "preferred_locale";
|
||||
private static Locale SYSTEM_DEFAULT_LOCALE = Locale.getDefault();
|
||||
|
||||
public static ArrayList<Locale> getAvailableLocales(Context context) {
|
||||
@@ -93,12 +92,13 @@ public class LocaleHelper {
|
||||
|
||||
public static String getPreferredLanguageTag(Context context) {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getString(PREFERRED_LOCALE_KEY, "");
|
||||
.getString("preferred_locale", "");
|
||||
// cannot access getString here as it's done BEFORE string locale is set
|
||||
}
|
||||
|
||||
@SuppressLint("ApplySharedPref")
|
||||
private static void savePreferredLangaugeTag(Context context, String locale) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit()
|
||||
.putString(PREFERRED_LOCALE_KEY, locale).commit();
|
||||
.putString(context.getString(R.string.preferred_locale), locale).commit();
|
||||
}
|
||||
}
|
||||
|
@@ -323,6 +323,7 @@ public class NetCipherHelper implements StatusCallback {
|
||||
}
|
||||
|
||||
private OkHttpClient getClient() {
|
||||
if (mockClient != null) return mockClient; // Unit-test mode
|
||||
final OkHttpClient client = getInstance().client;
|
||||
if ((username != null) && (!username.isEmpty())) {
|
||||
final DigestAuthenticator authenticator = new DigestAuthenticator(new Credentials(username, password));
|
||||
@@ -349,6 +350,9 @@ public class NetCipherHelper implements StatusCallback {
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
// for unit tests only
|
||||
static public OkHttpClient mockClient = null;
|
||||
}
|
||||
|
||||
private static final String PREFS_NAME = "tor";
|
||||
|
@@ -16,32 +16,37 @@
|
||||
|
||||
package com.m2049r.xmrwallet.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
|
||||
public class NightmodeHelper {
|
||||
private static final String PREFERRED_NIGHTMODE_KEY = "preferred_nightmode";
|
||||
import com.m2049r.xmrwallet.R;
|
||||
|
||||
public class NightmodeHelper {
|
||||
public static DayNightMode getPreferredNightmode(Context context) {
|
||||
return DayNightMode.valueOf(PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getString(PREFERRED_NIGHTMODE_KEY, "UNKNOWN"));
|
||||
.getString(context.getString(R.string.preferred_nightmode), "UNKNOWN"));
|
||||
}
|
||||
|
||||
public static void setPreferredNightmode(Context context) {
|
||||
final DayNightMode mode = DayNightMode.valueOf(PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getString(PREFERRED_NIGHTMODE_KEY, "UNKNOWN"));
|
||||
if (mode == DayNightMode.UNKNOWN) setAndSavePreferredNightmode(context, DayNightMode.AUTO);
|
||||
setNightMode(mode);
|
||||
.getString(context.getString(R.string.preferred_nightmode), "UNKNOWN"));
|
||||
if (mode == DayNightMode.UNKNOWN)
|
||||
setAndSavePreferredNightmode(context, DayNightMode.AUTO);
|
||||
else
|
||||
setNightMode(mode);
|
||||
}
|
||||
|
||||
public static void setAndSavePreferredNightmode(Context context, DayNightMode mode) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit()
|
||||
.putString(PREFERRED_NIGHTMODE_KEY, mode.name()).apply();
|
||||
.putString(context.getString(R.string.preferred_nightmode), mode.name()).apply();
|
||||
setNightMode(mode);
|
||||
}
|
||||
|
||||
@SuppressLint("WrongConstant")
|
||||
public static void setNightMode(DayNightMode mode) {
|
||||
AppCompatDelegate.setDefaultNightMode(mode.getNightMode());
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user