1
mirror of https://github.com/m2049r/xmrwallet synced 2025-09-17 19:10:51 +02:00

Compare commits

...

21 Commits

Author SHA1 Message Date
m2049r
5d489a634b bump version 2022-05-15 17:25:28 +02:00
m2049r
59b6f484fd update to monero v0.17.3.2 2022-05-15 17:24:54 +02:00
m2049r
ecaa49d67d upgrade build files 2022-05-15 15:57:11 +02:00
jont4
d2dc53599e Update PT-rBR strings (#813)
* Update pt-rBR

* Update app/src/main/res/values-pt-rBR/strings.xml

Co-authored-by: netrik182 <30935310+netrik182@users.noreply.github.com>

* Update app/src/main/res/values-pt-rBR/strings.xml

Co-authored-by: netrik182 <30935310+netrik182@users.noreply.github.com>

* Update strings.xml

fix as suggested

Co-authored-by: jontaix <31804298+jontaix@users.noreply.github.com>
Co-authored-by: netrik182 <30935310+netrik182@users.noreply.github.com>
Co-authored-by: m2049r <m2049r@monerujo.io>
2022-05-01 11:51:49 +02:00
Лапки
4d8b26f97f Update strings.xml (#826) 2022-05-01 11:44:52 +02:00
Justin Berman
581c76e7be setName uses async network helper in case of reverse DNS lookup (#818, #827) (#828) 2022-05-01 11:44:27 +02:00
AnonimaUzanto
6f66862870 Adding support for Ledger Nano S Plus (#832) 2022-05-01 11:35:01 +02:00
m2049r
dd92f7bb36 bump version 2022-03-13 15:37:28 +01:00
m2049r
46808d306b clean transitions 2022-03-12 23:10:34 +01:00
m2049r
20503d2cbd remove send/receive transitions 2022-03-11 00:25:50 +01:00
m2049r
604691ca7e fix FABs 2022-03-10 21:06:19 +01:00
m2049r
1b626ba2b0 improve transitions 2022-03-10 20:57:57 +01:00
m2049r
0ed7bdfcee remove test Dockerfile 2022-03-10 09:07:14 +01:00
m2049r
524c3dd79f upgrade ci image to 2022.03-ndk (#822) 2022-03-10 09:04:43 +01:00
m2049r
197dffeae1 fix tests 2022-03-10 07:53:52 +01:00
m2049r
cdb29bbc2e themes (#821)
* Support multiple colour themes
* Fix sharing
* Add settings
* Fantastic UI tweaks
2022-03-09 19:10:30 +01:00
m2049r
7b96baeca7 fix watch only 2022-01-03 12:53:31 +01:00
m2049r
0712efec78 allow seed offset generation & wallet restore (#804) 2022-01-02 20:50:07 +01:00
Katant Savelev
341df6c6a3 Update RU translation (#798)
* Update help.xml

* Update strings.xml
2022-01-02 14:10:33 +01:00
m2049r
ab8fb82c1b fix unit tests (#803) 2022-01-02 14:07:23 +01:00
m2049r
22d9173cea store only through service & setPassword (#802) 2022-01-02 13:05:46 +01:00
193 changed files with 1951 additions and 1043 deletions

View File

@@ -3,13 +3,11 @@ jobs:
build: build:
working_directory: ~/code working_directory: ~/code
docker: docker:
- image: circleci/android:api-28-ndk - image: cimg/android:2022.03-ndk
environment: environment:
JVM_OPTS: -Xmx3200m JVM_OPTS: -Xmx3200m
steps: steps:
- checkout - checkout
- run: yes | sdkmanager --licenses || exit 0
- run: yes | sdkmanager --update || exit 0
- restore_cache: - restore_cache:
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }} key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
- run: - run:

View File

@@ -1,15 +1,15 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
android { android {
compileSdkVersion 30 compileSdkVersion 31
buildToolsVersion '30.0.3' buildToolsVersion '30.0.3'
ndkVersion '17.2.4988734' ndkVersion '17.2.4988734'
defaultConfig { defaultConfig {
applicationId "com.m2049r.xmrwallet" applicationId "com.m2049r.xmrwallet"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 30 targetSdkVersion 31
versionCode 1201 versionCode 1402
versionName "2.2.1 'René'" versionName "2.4.2 'Baldaŭ'"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild { externalNativeBuild {
cmake { cmake {
@@ -111,6 +111,7 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
namespace 'com.m2049r.xmrwallet'
} }
static def getId(name) { static def getId(name) {
@@ -120,38 +121,39 @@ static def getId(name) {
} }
dependencies { dependencies {
implementation 'androidx.core:core:1.6.0' implementation 'androidx.core:core:1.7.0'
implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'androidx.appcompat:appcompat:1.4.1'
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.2.1' 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.1.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1' 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 'me.dm7.barcodescanner:zxing:1.9.8'
implementation "com.squareup.okhttp3:okhttp:4.9.0" implementation "com.squareup.okhttp3:okhttp:4.9.3"
implementation "io.github.rburgst:okhttp-digest:2.5" implementation "io.github.rburgst:okhttp-digest:2.6"
implementation "com.jakewharton.timber:timber:4.7.1" implementation "com.jakewharton.timber:timber:5.0.1"
implementation 'info.guardianproject.netcipher:netcipher:2.1.0' implementation 'info.guardianproject.netcipher:netcipher:2.1.0'
//implementation 'info.guardianproject.netcipher:netcipher-okhttp3:2.1.0' //implementation 'info.guardianproject.netcipher:netcipher-okhttp3:2.1.0'
implementation fileTree(dir: 'libs/classes', include: ['*.jar']) 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 'dnsjava:dnsjava:2.1.9'
implementation 'org.jitsi:dnssecjava:1.2.0' 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' implementation 'com.github.brnunes:swipeablerecyclerview:1.0.2'
//noinspection GradleDependency //noinspection GradleDependency
testImplementation "junit:junit:$rootProject.ext.junitVersion" testImplementation "junit:junit:4.13.2"
testImplementation "org.mockito:mockito-all:$rootProject.ext.mockitoVersion" testImplementation "org.mockito:mockito-all:1.10.19"
testImplementation "com.squareup.okhttp3:mockwebserver:4.9.0" testImplementation "com.squareup.okhttp3:mockwebserver:4.9.3"
testImplementation 'org.json:json:20180813' testImplementation 'org.json:json:20211205'
testImplementation 'net.jodah:concurrentunit:0.4.4' testImplementation 'net.jodah:concurrentunit:0.4.6'
compileOnly 'org.projectlombok:lombok:1.18.16' compileOnly 'org.projectlombok:lombok:1.18.22'
annotationProcessor 'org.projectlombok:lombok:1.18.16' annotationProcessor 'org.projectlombok:lombok:1.18.22'
} }

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android">
package="com.m2049r.xmrwallet">
<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" />
@@ -18,7 +17,6 @@
<intent> <intent>
<action android:name="org.torproject.android.intent.action.STATUS" /> <action android:name="org.torproject.android.intent.action.STATUS" />
</intent> </intent>
<intent> <intent>
<action android:name="org.torproject.android.REQUEST_HS_PORT" /> <action android:name="org.torproject.android.REQUEST_HS_PORT" />
</intent> </intent>
@@ -36,15 +34,17 @@
android:label="@string/app_name" android:label="@string/app_name"
android:preserveLegacyExternalStorage="true" android:preserveLegacyExternalStorage="true"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/MyMaterialTheme" android:theme="@style/MyMaterialThemeClassic"
android:usesCleartextTraffic="true"> android:usesCleartextTraffic="true">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|uiMode" android:configChanges="orientation|keyboardHidden|uiMode"
android:exported="true"
android:launchMode="singleTop" android:launchMode="singleTop"
android:screenOrientation="portrait"> android:screenOrientation="portrait">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
@@ -56,24 +56,25 @@
android:screenOrientation="behind" /> android:screenOrientation="behind" />
<activity <activity
android:name=".LoginActivity" android:name=".LoginActivity"
android:configChanges="orientation|keyboardHidden|uiMode" android:configChanges="orientation|keyboardHidden"
android:exported="true"
android:label="@string/app_name" android:label="@string/app_name"
android:launchMode="singleTop" android:launchMode="singleTop"
android:screenOrientation="locked"> android:screenOrientation="locked">
<intent-filter> <intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter> </intent-filter>
<intent-filter android:label="@string/app_name"> <intent-filter android:label="@string/app_name">
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<data android:scheme="monero" /> <data android:scheme="monero" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
</intent-filter> </intent-filter>
<intent-filter android:label="@string/app_name"> <intent-filter android:label="@string/app_name">
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<data android:scheme="bitcoin" /> <data android:scheme="bitcoin" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
@@ -106,4 +107,5 @@
android:resource="@xml/filepaths" /> android:resource="@xml/filepaths" />
</provider> </provider>
</application> </application>
</manifest>
</manifest>

View File

@@ -229,7 +229,7 @@ std::vector<std::string> java2cpp(JNIEnv *env, jobject arrayList) {
return result; 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_ = env->GetMethodID(class_ArrayList, "<init>", "(I)V");
jmethodID java_util_ArrayList_add = env->GetMethodID(class_ArrayList, "add", 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 JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_recoveryWalletJ(JNIEnv *env, jobject instance, Java_com_m2049r_xmrwallet_model_WalletManager_recoveryWalletJ(JNIEnv *env, jobject instance,
jstring path, jstring password, jstring path, jstring password,
jstring mnemonic, jstring mnemonic, jstring offset,
jint networkType, jint networkType,
jlong restoreHeight) { jlong restoreHeight) {
const char *_path = env->GetStringUTFChars(path, nullptr); const char *_path = env->GetStringUTFChars(path, nullptr);
const char *_password = env->GetStringUTFChars(password, nullptr); const char *_password = env->GetStringUTFChars(password, nullptr);
const char *_mnemonic = env->GetStringUTFChars(mnemonic, nullptr); const char *_mnemonic = env->GetStringUTFChars(mnemonic, nullptr);
const char *_offset = env->GetStringUTFChars(offset, nullptr);
Monero::NetworkType _networkType = static_cast<Monero::NetworkType>(networkType); Monero::NetworkType _networkType = static_cast<Monero::NetworkType>(networkType);
Bitmonero::Wallet *wallet = Bitmonero::Wallet *wallet =
@@ -315,11 +316,14 @@ Java_com_m2049r_xmrwallet_model_WalletManager_recoveryWalletJ(JNIEnv *env, jobje
std::string(_password), std::string(_password),
std::string(_mnemonic), std::string(_mnemonic),
_networkType, _networkType,
(uint64_t) restoreHeight); (uint64_t) restoreHeight,
1, // kdf_rounds
std::string(_offset));
env->ReleaseStringUTFChars(path, _path); env->ReleaseStringUTFChars(path, _path);
env->ReleaseStringUTFChars(password, _password); env->ReleaseStringUTFChars(password, _password);
env->ReleaseStringUTFChars(mnemonic, _mnemonic); env->ReleaseStringUTFChars(mnemonic, _mnemonic);
env->ReleaseStringUTFChars(offset, _offset);
return reinterpret_cast<jlong>(wallet); return reinterpret_cast<jlong>(wallet);
} }
@@ -533,7 +537,7 @@ Java_com_m2049r_xmrwallet_model_WalletManager_resolveOpenAlias(JNIEnv *env, jobj
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_setProxy(JNIEnv *env, jobject instance, Java_com_m2049r_xmrwallet_model_WalletManager_setProxy(JNIEnv *env, jobject instance,
jstring address) { jstring address) {
const char *_address = env->GetStringUTFChars(address, nullptr); const char *_address = env->GetStringUTFChars(address, nullptr);
bool rc = bool rc =
Bitmonero::WalletManagerFactory::getWalletManager()->setProxy(std::string(_address)); 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 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); 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 JNIEXPORT jstring JNICALL
@@ -740,7 +747,7 @@ Java_com_m2049r_xmrwallet_model_Wallet_getConnectionStatusJ(JNIEnv *env, jobject
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_setProxy(JNIEnv *env, jobject instance, Java_com_m2049r_xmrwallet_model_Wallet_setProxy(JNIEnv *env, jobject instance,
jstring address) { jstring address) {
const char *_address = env->GetStringUTFChars(address, nullptr); const char *_address = env->GetStringUTFChars(address, nullptr);
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance); Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
bool rc = wallet->setProxy(std::string(_address)); bool rc = wallet->setProxy(std::string(_address));
@@ -1262,7 +1269,7 @@ jobject newTransactionInfo(JNIEnv *env, Bitmonero::TransactionInfo *info) {
#include <stdio.h> #include <stdio.h>
#include <stdlib.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_ = env->GetMethodID(class_ArrayList, "<init>", "(I)V");
jmethodID java_util_ArrayList_add = env->GetMethodID(class_ArrayList, "add", jmethodID java_util_ArrayList_add = env->GetMethodID(class_ArrayList, "add",

View File

@@ -78,7 +78,7 @@ public class BTChipTransportAndroidHID implements BTChipTransport {
} }
private static final int VID = 0x2C97; 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 UsbDeviceConnection connection;
private UsbInterface dongleInterface; private UsbInterface dongleInterface;

View File

@@ -27,6 +27,7 @@ import android.nfc.NfcAdapter;
import android.nfc.Tag; import android.nfc.Tag;
import android.nfc.tech.Ndef; import android.nfc.tech.Ndef;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
@@ -180,7 +181,7 @@ public class BaseActivity extends SecureActivity
return; return;
nfcPendingIntent = PendingIntent.getActivity(this, 0, nfcPendingIntent = PendingIntent.getActivity(this, 0,
new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 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) { private void processNfcIntent(Intent intent) {

View File

@@ -36,7 +36,6 @@ import android.view.WindowManager;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.widget.Button; import android.widget.Button;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
@@ -58,7 +57,6 @@ import com.m2049r.xmrwallet.widget.Toolbar;
import java.io.File; import java.io.File;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Objects;
import timber.log.Timber; import timber.log.Timber;
@@ -81,6 +79,9 @@ public class GenerateFragment extends Fragment {
private TextInputLayout etWalletRestoreHeight; private TextInputLayout etWalletRestoreHeight;
private Button bGenerate; private Button bGenerate;
private Button bSeedOffset;
private TextInputLayout etSeedOffset;
private String type = null; private String type = null;
private void clearErrorOnTextEntry(final TextInputLayout textInputLayout) { private void clearErrorOnTextEntry(final TextInputLayout textInputLayout) {
@@ -118,6 +119,8 @@ public class GenerateFragment extends Fragment {
etWalletSpendKey = view.findViewById(R.id.etWalletSpendKey); etWalletSpendKey = view.findViewById(R.id.etWalletSpendKey);
etWalletRestoreHeight = view.findViewById(R.id.etWalletRestoreHeight); etWalletRestoreHeight = view.findViewById(R.id.etWalletRestoreHeight);
bGenerate = view.findViewById(R.id.bGenerate); 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); etWalletAddress.getEditText().setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
etWalletViewKey.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; return false;
}); });
bSeedOffset.setVisibility(View.VISIBLE);
bSeedOffset.setOnClickListener(v -> toggleSeedOffset());
break; break;
case TYPE_KEY: case TYPE_KEY:
case TYPE_VIEWONLY: case TYPE_VIEWONLY:
@@ -288,6 +293,18 @@ public class GenerateFragment extends Fragment {
return view; 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() { private boolean checkName() {
String name = etWalletName.getEditText().getText().toString(); String name = etWalletName.getEditText().getText().toString();
boolean ok = true; boolean ok = true;
@@ -422,12 +439,13 @@ public class GenerateFragment extends Fragment {
break; break;
case TYPE_SEED: case TYPE_SEED:
if (!checkMnemonic()) return; if (!checkMnemonic()) return;
String seed = etWalletMnemonic.getEditText().getText().toString(); final String seed = etWalletMnemonic.getEditText().getText().toString();
bGenerate.setEnabled(false); bGenerate.setEnabled(false);
if (fingerprintAuthAllowed) { if (fingerprintAuthAllowed) {
KeyStoreHelper.saveWalletUserPass(requireActivity(), name, password); 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; break;
case TYPE_LEDGER: case TYPE_LEDGER:
bGenerate.setEnabled(false); bGenerate.setEnabled(false);
@@ -491,7 +509,7 @@ public class GenerateFragment extends Fragment {
public interface Listener { public interface Listener {
void onGenerate(String name, String password); 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); void onGenerate(String name, String password, String address, String viewKey, String spendKey, long height);

View File

@@ -26,6 +26,7 @@ import android.content.SharedPreferences;
import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager; import android.hardware.usb.UsbManager;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@@ -39,7 +40,6 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction; 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.DefaultNodes;
import com.m2049r.xmrwallet.data.Node; import com.m2049r.xmrwallet.data.Node;
import com.m2049r.xmrwallet.data.NodeInfo; import com.m2049r.xmrwallet.data.NodeInfo;
import com.m2049r.xmrwallet.dialog.AboutFragment;
import com.m2049r.xmrwallet.dialog.CreditsFragment; import com.m2049r.xmrwallet.dialog.CreditsFragment;
import com.m2049r.xmrwallet.dialog.HelpFragment; import com.m2049r.xmrwallet.dialog.HelpFragment;
import com.m2049r.xmrwallet.dialog.PrivacyFragment;
import com.m2049r.xmrwallet.ledger.Ledger; import com.m2049r.xmrwallet.ledger.Ledger;
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog; import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
import com.m2049r.xmrwallet.model.NetworkType; import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager; import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.service.WalletService; import com.m2049r.xmrwallet.service.WalletService;
import com.m2049r.xmrwallet.util.DayNightMode;
import com.m2049r.xmrwallet.util.Helper; import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.KeyStoreHelper; import com.m2049r.xmrwallet.util.KeyStoreHelper;
import com.m2049r.xmrwallet.util.LegacyStorageHelper; import com.m2049r.xmrwallet.util.LegacyStorageHelper;
import com.m2049r.xmrwallet.util.LocaleHelper;
import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor; import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor;
import com.m2049r.xmrwallet.util.NetCipherHelper; import com.m2049r.xmrwallet.util.NetCipherHelper;
import com.m2049r.xmrwallet.util.NightmodeHelper;
import com.m2049r.xmrwallet.util.ThemeHelper; import com.m2049r.xmrwallet.util.ThemeHelper;
import com.m2049r.xmrwallet.util.ZipBackup; import com.m2049r.xmrwallet.util.ZipBackup;
import com.m2049r.xmrwallet.util.ZipRestore; import com.m2049r.xmrwallet.util.ZipRestore;
@@ -77,12 +72,8 @@ import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -90,7 +81,8 @@ import timber.log.Timber;
public class LoginActivity extends BaseActivity public class LoginActivity extends BaseActivity
implements LoginFragment.Listener, GenerateFragment.Listener, 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 GENERATE_STACK = "gen";
private static final String NODES_PREFS_NAME = "nodes"; private static final String NODES_PREFS_NAME = "nodes";
@@ -293,6 +285,7 @@ public class LoginActivity extends BaseActivity
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
Timber.d("onCreate()"); Timber.d("onCreate()");
ThemeHelper.setPreferred(this);
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login); setContentView(R.layout.activity_login);
@@ -308,8 +301,8 @@ public class LoginActivity extends BaseActivity
case Toolbar.BUTTON_CLOSE: case Toolbar.BUTTON_CLOSE:
finish(); finish();
break; break;
case Toolbar.BUTTON_CREDITS: case Toolbar.BUTTON_SETTINGS:
CreditsFragment.display(getSupportFragmentManager()); startSettingsFragment();
break; break;
case Toolbar.BUTTON_NONE: case Toolbar.BUTTON_NONE:
break; break;
@@ -785,6 +778,11 @@ public class LoginActivity extends BaseActivity
Timber.d("NodeFragment placed"); Timber.d("NodeFragment placed");
} }
void startSettingsFragment() {
replaceFragment(new SettingsFragment(), null, null);
Timber.d("SettingsFragment placed");
}
void replaceFragment(Fragment newFragment, String stackName, Bundle extras) { void replaceFragment(Fragment newFragment, String stackName, Bundle extras) {
if (extras != null) { if (extras != null) {
newFragment.setArguments(extras); newFragment.setArguments(extras);
@@ -929,7 +927,8 @@ public class LoginActivity extends BaseActivity
} }
@Override @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) { final long restoreHeight) {
createWallet(name, password, createWallet(name, password,
new WalletCreator() { new WalletCreator() {
@@ -941,7 +940,7 @@ public class LoginActivity extends BaseActivity
@Override @Override
public boolean createWallet(File aFile, String password) { public boolean createWallet(File aFile, String password) {
Wallet newWallet = WalletManager.getInstance() Wallet newWallet = WalletManager.getInstance()
.recoveryWallet(aFile, password, seed, restoreHeight); .recoveryWallet(aFile, password, seed, offset, restoreHeight);
return checkAndCloseWallet(newWallet); 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 @Override
public void onBackPressed() { public void onBackPressed() {
Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container); 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) { } else if (id == R.id.action_details_changepw) {
onWalletChangePassword(); onWalletChangePassword();
return true; return true;
} else if (id == R.id.action_license_info) {
AboutFragment.display(getSupportFragmentManager());
return true;
} else if (id == R.id.action_help_list) { } else if (id == R.id.action_help_list) {
HelpFragment.display(getSupportFragmentManager(), R.string.help_list); HelpFragment.display(getSupportFragmentManager(), R.string.help_list);
return true; return true;
@@ -1207,18 +1148,6 @@ public class LoginActivity extends BaseActivity
((NodeFragment) f).restoreDefaultNodes(); ((NodeFragment) f).restoreDefaultNodes();
} }
return true; 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) { } else if (id == R.id.action_ledger_seed) {
Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container); Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if (f instanceof GenerateFragment) { if (f instanceof GenerateFragment) {
@@ -1332,7 +1261,8 @@ public class LoginActivity extends BaseActivity
registerReceiver(usbPermissionReceiver, new IntentFilter(ACTION_USB_PERMISSION)); registerReceiver(usbPermissionReceiver, new IntentFilter(ACTION_USB_PERMISSION));
usbManager.requestPermission(device, usbManager.requestPermission(device,
PendingIntent.getBroadcast(this, 0, 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 { } else {
Timber.d("no ledger device found"); Timber.d("no ledger device found");

View File

@@ -38,7 +38,6 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView; 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.NetCipherHelper;
import com.m2049r.xmrwallet.util.NodePinger; import com.m2049r.xmrwallet.util.NodePinger;
import com.m2049r.xmrwallet.util.Notice; import com.m2049r.xmrwallet.util.Notice;
import com.m2049r.xmrwallet.util.ThemeHelper;
import com.m2049r.xmrwallet.widget.Toolbar; import com.m2049r.xmrwallet.widget.Toolbar;
import java.io.File; import java.io.File;
@@ -143,7 +143,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
super.onResume(); super.onResume();
Timber.d("onResume() %s", activityCallback.getFavouriteNodes().size()); Timber.d("onResume() %s", activityCallback.getFavouriteNodes().size());
activityCallback.setTitle(null); activityCallback.setTitle(null);
activityCallback.setToolbarButton(Toolbar.BUTTON_CREDITS); activityCallback.setToolbarButton(Toolbar.BUTTON_SETTINGS);
activityCallback.showNet(); activityCallback.showNet();
showNetwork(); showNetwork();
//activityCallback.runOnNetCipher(this::pingSelectedNode); //activityCallback.runOnNetCipher(this::pingSelectedNode);
@@ -163,12 +163,14 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
fabView = view.findViewById(R.id.fabView); fabView = view.findViewById(R.id.fabView);
fabKey = view.findViewById(R.id.fabKey); fabKey = view.findViewById(R.id.fabKey);
fabSeed = view.findViewById(R.id.fabSeed); fabSeed = view.findViewById(R.id.fabSeed);
fabImport = view.findViewById(R.id.fabImport);
fabLedger = view.findViewById(R.id.fabLedger); fabLedger = view.findViewById(R.id.fabLedger);
fabNewL = view.findViewById(R.id.fabNewL); fabNewL = view.findViewById(R.id.fabNewL);
fabViewL = view.findViewById(R.id.fabViewL); fabViewL = view.findViewById(R.id.fabViewL);
fabKeyL = view.findViewById(R.id.fabKeyL); fabKeyL = view.findViewById(R.id.fabKeyL);
fabSeedL = view.findViewById(R.id.fabSeedL); fabSeedL = view.findViewById(R.id.fabSeedL);
fabImportL = view.findViewById(R.id.fabImportL);
fabLedgerL = view.findViewById(R.id.fabLedgerL); fabLedgerL = view.findViewById(R.id.fabLedgerL);
fab_pulse = AnimationUtils.loadAnimation(getContext(), R.anim.fab_pulse); fab_pulse = AnimationUtils.loadAnimation(getContext(), R.anim.fab_pulse);
@@ -183,6 +185,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
fabView.setOnClickListener(this); fabView.setOnClickListener(this);
fabKey.setOnClickListener(this); fabKey.setOnClickListener(this);
fabSeed.setOnClickListener(this); fabSeed.setOnClickListener(this);
fabImport.setOnClickListener(this);
fabLedger.setOnClickListener(this); fabLedger.setOnClickListener(this);
fabScreen.setOnClickListener(this); fabScreen.setOnClickListener(this);
@@ -294,9 +297,9 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
} }
private boolean isFabOpen = false; private boolean isFabOpen = false;
private FloatingActionButton fab, fabNew, fabView, fabKey, fabSeed, fabLedger; private FloatingActionButton fab, fabNew, fabView, fabKey, fabSeed, fabImport, fabLedger;
private FrameLayout fabScreen; private RelativeLayout fabScreen;
private RelativeLayout fabNewL, fabViewL, fabKeyL, fabSeedL, fabLedgerL; 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_open, fab_close, rotate_forward, rotate_backward, fab_open_screen, fab_close_screen;
private Animation fab_pulse; private Animation fab_pulse;
@@ -321,6 +324,8 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
fabKey.setClickable(false); fabKey.setClickable(false);
fabSeedL.startAnimation(fab_close); fabSeedL.startAnimation(fab_close);
fabSeed.setClickable(false); fabSeed.setClickable(false);
fabImportL.startAnimation(fab_close);
fabImport.setClickable(false);
} }
isFabOpen = false; isFabOpen = false;
} else { // open the fab } else { // open the fab
@@ -333,6 +338,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
fabViewL.setVisibility(View.GONE); fabViewL.setVisibility(View.GONE);
fabKeyL.setVisibility(View.GONE); fabKeyL.setVisibility(View.GONE);
fabSeedL.setVisibility(View.GONE); fabSeedL.setVisibility(View.GONE);
fabImportL.setVisibility(View.GONE);
fabLedgerL.startAnimation(fab_open); fabLedgerL.startAnimation(fab_open);
fabLedger.setClickable(true); fabLedger.setClickable(true);
@@ -342,6 +348,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
fabViewL.setVisibility(View.VISIBLE); fabViewL.setVisibility(View.VISIBLE);
fabKeyL.setVisibility(View.VISIBLE); fabKeyL.setVisibility(View.VISIBLE);
fabSeedL.setVisibility(View.VISIBLE); fabSeedL.setVisibility(View.VISIBLE);
fabImportL.setVisibility(View.VISIBLE);
fabNewL.startAnimation(fab_open); fabNewL.startAnimation(fab_open);
fabNew.setClickable(true); fabNew.setClickable(true);
@@ -351,6 +358,8 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
fabKey.setClickable(true); fabKey.setClickable(true);
fabSeedL.startAnimation(fab_open); fabSeedL.startAnimation(fab_open);
fabSeed.setClickable(true); fabSeed.setClickable(true);
fabImportL.startAnimation(fab_open);
fabImport.setClickable(true);
} }
isFabOpen = true; isFabOpen = true;
} }
@@ -375,6 +384,9 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
} else if (id == R.id.fabSeed) { } else if (id == R.id.fabSeed) {
animateFAB(); animateFAB();
activityCallback.onAddWallet(GenerateFragment.TYPE_SEED); activityCallback.onAddWallet(GenerateFragment.TYPE_SEED);
} else if (id == R.id.fabImport) {
animateFAB();
activityCallback.onWalletRestore();
} else if (id == R.id.fabLedger) { } else if (id == R.id.fabLedger) {
Timber.d("FAB_LEDGER"); Timber.d("FAB_LEDGER");
animateFAB(); animateFAB();
@@ -403,8 +415,8 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
private void setSubtext(String status) { private void setSubtext(String status) {
final Context ctx = getContext(); final Context ctx = getContext();
final Spanned text = Html.fromHtml(ctx.getString(R.string.status, final Spanned text = Html.fromHtml(ctx.getString(R.string.status,
Integer.toHexString(ContextCompat.getColor(ctx, R.color.monerujoGreen) & 0xFFFFFF), Integer.toHexString(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor) & 0xFFFFFF),
Integer.toHexString(ContextCompat.getColor(ctx, R.color.monerujoBackground) & 0xFFFFFF), Integer.toHexString(ThemeHelper.getThemedColor(ctx, android.R.attr.colorBackground) & 0xFFFFFF),
status, "")); status, ""));
tvNodeInfo.setText(text); tvNodeInfo.setText(text);
} }

View File

@@ -415,7 +415,14 @@ public class NodeFragment extends Fragment
} }
etNodeHost.setError(null); etNodeHost.setError(null);
nodeInfo.setRpcPort(port); 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.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;

View File

@@ -46,10 +46,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.widget.ShareActionProvider;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider; import androidx.core.content.FileProvider;
import androidx.core.view.MenuItemCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.google.android.material.textfield.TextInputLayout; import com.google.android.material.textfield.TextInputLayout;
@@ -89,6 +86,7 @@ public class ReceiveFragment extends Fragment {
private ImageView ivQrCodeFull; private ImageView ivQrCodeFull;
private EditText etDummy; private EditText etDummy;
private ImageButton bCopyAddress; private ImageButton bCopyAddress;
private MenuItem shareItem;
private Wallet wallet = null; private Wallet wallet = null;
private boolean isMyWallet = false; private boolean isMyWallet = false;
@@ -128,6 +126,7 @@ public class ReceiveFragment extends Fragment {
evAmount.setOnNewAmountListener(xmr -> { evAmount.setOnNewAmountListener(xmr -> {
Timber.d("new amount = %s", xmr); Timber.d("new amount = %s", xmr);
generateQr(); generateQr();
if (shareRequested && (xmr != null)) share();
}); });
evAmount.setOnFailedExchangeListener(() -> { evAmount.setOnFailedExchangeListener(() -> {
@@ -211,39 +210,38 @@ public class ReceiveFragment extends Fragment {
setSharedElementEnterTransition(transform); setSharedElementEnterTransition(transform);
} }
private ShareActionProvider shareActionProvider; private boolean shareRequested = false;
@Override @Override
public void onCreateOptionsMenu(@NonNull Menu menu, final MenuInflater inflater) { public void onCreateOptionsMenu(@NonNull Menu menu, final MenuInflater inflater) {
inflater.inflate(R.menu.receive_menu, menu); inflater.inflate(R.menu.receive_menu, menu);
super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater);
// Locate MenuItem with ShareActionProvider shareItem = menu.findItem(R.id.menu_item_share);
MenuItem item = menu.findItem(R.id.menu_item_share); shareItem.setOnMenuItemClickListener(item -> {
if (shareRequested) return true;
// Fetch and store ShareActionProvider shareRequested = true;
shareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(item); if (!qrValid) {
evAmount.doExchange();
shareActionProvider.setOnShareTargetSelectedListener(new ShareActionProvider.OnShareTargetSelectedListener() { } else {
@Override share();
public boolean onShareTargetSelected(ShareActionProvider shareActionProvider, Intent intent) {
saveQrCode(); // save it only if we need it
return false;
} }
return true;
}); });
} }
private void setShareIntent() { private void share() {
if (shareActionProvider != null) { shareRequested = false;
if (qrValid) { if (saveQrCode()) {
shareActionProvider.setShareIntent(getShareIntent()); final Intent sendIntent = getSendIntent();
} else { if (sendIntent != null)
shareActionProvider.setShareIntent(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!"); if (!qrValid) throw new IllegalStateException("trying to save null qr code!");
File cachePath = new File(getActivity().getCacheDir(), "images"); File cachePath = new File(getActivity().getCacheDir(), "images");
@@ -255,33 +253,35 @@ public class ReceiveFragment extends Fragment {
Bitmap qrBitmap = ((BitmapDrawable) ivQrCode.getDrawable()).getBitmap(); Bitmap qrBitmap = ((BitmapDrawable) ivQrCode.getDrawable()).getBitmap();
qrBitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); qrBitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
stream.close(); stream.close();
return true;
} catch (IOException ex) { } catch (IOException ex) {
Timber.e(ex); Timber.e(ex);
// make sure we don't share an old qr code // make sure we don't share an old qr code
if (!png.delete()) throw new IllegalStateException("cannot delete 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 // 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() { private Intent getSendIntent() {
File imagePath = new File(getActivity().getCacheDir(), "images"); File imagePath = new File(requireActivity().getCacheDir(), "images");
File png = new File(imagePath, "QR.png"); File png = new File(imagePath, "QR.png");
Uri contentUri = FileProvider.getUriForFile(getActivity(), Uri contentUri = FileProvider.getUriForFile(requireActivity(), BuildConfig.APPLICATION_ID + ".fileprovider", png);
BuildConfig.APPLICATION_ID + ".fileprovider", png);
if (contentUri != null) { if (contentUri != null) {
Intent shareIntent = new Intent(); Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND); shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // temp permission for receiving app to read this file 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_STREAM, contentUri);
shareIntent.putExtra(Intent.EXTRA_TEXT, bcData.getUriString()); if (bcData != null)
shareIntent.putExtra(Intent.EXTRA_TEXT, bcData.getUriString());
return shareIntent; return shareIntent;
} }
return null; return null;
} }
void copyAddress() { 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(); Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show();
} }
@@ -291,7 +291,6 @@ public class ReceiveFragment extends Fragment {
if (qrValid) { if (qrValid) {
ivQrCode.setImageBitmap(null); ivQrCode.setImageBitmap(null);
qrValid = false; qrValid = false;
setShareIntent();
if (isLoaded) if (isLoaded)
tvQrCode.setVisibility(View.VISIBLE); tvQrCode.setVisibility(View.VISIBLE);
} }
@@ -300,7 +299,6 @@ public class ReceiveFragment extends Fragment {
void setQR(Bitmap qr) { void setQR(Bitmap qr) {
ivQrCode.setImageBitmap(qr); ivQrCode.setImageBitmap(qr);
qrValid = true; qrValid = true;
setShareIntent();
tvQrCode.setVisibility(View.GONE); tvQrCode.setVisibility(View.GONE);
} }
@@ -462,8 +460,8 @@ public class ReceiveFragment extends Fragment {
subaddress = newSubaddress; subaddress = newSubaddress;
final Context context = getContext(); final Context context = getContext();
Spanned label = Html.fromHtml(context.getString(R.string.receive_subaddress, Spanned label = Html.fromHtml(context.getString(R.string.receive_subaddress,
Integer.toHexString(ContextCompat.getColor(context, R.color.monerujoGreen) & 0xFFFFFF), Integer.toHexString(ThemeHelper.getThemedColor(context, R.attr.positiveColor) & 0xFFFFFF),
Integer.toHexString(ContextCompat.getColor(context, R.color.monerujoBackground) & 0xFFFFFF), Integer.toHexString(ThemeHelper.getThemedColor(context, android.R.attr.colorBackground) & 0xFFFFFF),
subaddress.getDisplayLabel(), subaddress.getAddress())); subaddress.getDisplayLabel(), subaddress.getAddress()));
tvAddress.setText(label); tvAddress.setText(label);
generateQr(); generateQr();

View File

@@ -66,11 +66,7 @@ public abstract class SecureActivity extends AppCompatActivity {
Locale locale = LocaleHelper.getPreferredLocale(this); Locale locale = LocaleHelper.getPreferredLocale(this);
if (locale != null) { if (locale != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { config.setLocale(locale);
config.setLocale(locale);
} else {
config.locale = locale;
}
} }
return config; return config;
} }

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

View File

@@ -30,7 +30,6 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.transition.MaterialElevationScale;
import com.m2049r.xmrwallet.data.Subaddress; import com.m2049r.xmrwallet.data.Subaddress;
import com.m2049r.xmrwallet.layout.SubaddressInfoAdapter; import com.m2049r.xmrwallet.layout.SubaddressInfoAdapter;
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog; import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
@@ -67,6 +66,8 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
void setToolbarButton(int type); void setToolbarButton(int type);
void showSubaddress(View view, final int subaddressIndex); void showSubaddress(View view, final int subaddressIndex);
void saveWallet();
} }
public interface ProgressListener { public interface ProgressListener {
@@ -115,14 +116,6 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
managerMode = ((b != null) && (MODE_MANAGER.equals(b.getString(KEY_MODE)))); managerMode = ((b != null) && (MODE_MANAGER.equals(b.getString(KEY_MODE))));
View view = inflater.inflate(R.layout.fragment_subaddress, container, false); 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); view.findViewById(R.id.fab).setOnClickListener(this);
if (managerMode) { if (managerMode) {
@@ -152,11 +145,6 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
postponeEnterTransition();
view.getViewTreeObserver().addOnPreDrawListener(() -> {
startPostponedEnterTransition();
return true;
});
} }
public void loadList() { public void loadList() {
@@ -217,7 +205,9 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
protected Boolean doInBackground(Void... params) { protected Boolean doInBackground(Void... params) {
if (params.length != 0) return false; if (params.length != 0) return false;
wallet.getNewSubaddress(); wallet.getNewSubaddress();
wallet.store(); if (activityCallback != null) {
activityCallback.saveWallet();
}
return true; return true;
} }

View File

@@ -29,15 +29,15 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView; 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.textfield.TextInputLayout;
import com.google.android.material.transition.MaterialContainerTransform;
import com.m2049r.xmrwallet.data.Subaddress; import com.m2049r.xmrwallet.data.Subaddress;
import com.m2049r.xmrwallet.layout.TransactionInfoAdapter; import com.m2049r.xmrwallet.layout.TransactionInfoAdapter;
import com.m2049r.xmrwallet.model.TransactionInfo; import com.m2049r.xmrwallet.model.TransactionInfo;
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.util.ArrayList; import java.util.ArrayList;
@@ -102,10 +102,8 @@ public class SubaddressInfoFragment extends Fragment
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
final MaterialContainerTransform transform = new MaterialContainerTransform(); Transition transform = TransitionInflater.from(requireContext())
transform.setDrawingViewId(R.id.fragment_container); .inflateTransition(R.transition.details);
transform.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
transform.setAllContainerColors(ThemeHelper.getThemedColor(getContext(), android.R.attr.colorBackground));
setSharedElementEnterTransition(transform); setSharedElementEnterTransition(transform);
} }

View File

@@ -35,11 +35,10 @@ import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; 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.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;
@@ -97,13 +96,6 @@ public class TxFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, 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);
@@ -220,8 +212,8 @@ public class TxFragment extends Fragment {
final Context ctx = getContext(); final Context ctx = getContext();
Spanned label = Html.fromHtml(ctx.getString(R.string.tx_account_formatted, Spanned label = Html.fromHtml(ctx.getString(R.string.tx_account_formatted,
info.accountIndex, info.addressIndex, info.accountIndex, info.addressIndex,
Integer.toHexString(ContextCompat.getColor(ctx, R.color.monerujoGreen) & 0xFFFFFF), Integer.toHexString(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor) & 0xFFFFFF),
Integer.toHexString(ContextCompat.getColor(ctx, R.color.monerujoBackground) & 0xFFFFFF), Integer.toHexString(ThemeHelper.getThemedColor(ctx, android.R.attr.colorBackground) & 0xFFFFFF),
subaddress.getDisplayLabel())); subaddress.getDisplayLabel()));
tvAccount.setText(label); tvAccount.setText(label);
tvAccount.setOnClickListener(v -> activityCallback.showSubaddress(v, info.addressIndex)); tvAccount.setOnClickListener(v -> activityCallback.showSubaddress(v, info.addressIndex));
@@ -266,13 +258,13 @@ public class TxFragment extends Fragment {
if (info.isFailed) { if (info.isFailed) {
tvTxAmount.setText(getString(R.string.tx_list_amount_failed, Wallet.getDisplayAmount(info.amount))); tvTxAmount.setText(getString(R.string.tx_list_amount_failed, Wallet.getDisplayAmount(info.amount)));
tvTxFee.setText(getString(R.string.tx_list_failed_text)); 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) { } 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) { } else if (info.direction == TransactionInfo.Direction.Direction_In) {
setTxColour(ContextCompat.getColor(getContext(), R.color.tx_plus)); setTxColour(ThemeHelper.getThemedColor(getContext(), R.attr.positiveColor));
} else { } else {
setTxColour(ContextCompat.getColor(getContext(), R.color.tx_minus)); setTxColour(ThemeHelper.getThemedColor(getContext(), R.attr.negativeColor));
} }
Set<String> destinations = new HashSet<>(); Set<String> destinations = new HashSet<>();
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
@@ -348,10 +340,8 @@ 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(); Transition transform = TransitionInflater.from(requireContext())
transform.setDrawingViewId(R.id.fragment_container); .inflateTransition(R.transition.details);
transform.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
transform.setAllContainerColors(ThemeHelper.getThemedColor(getContext(), android.R.attr.colorBackground));
setSharedElementEnterTransition(transform); setSharedElementEnterTransition(transform);
} }

View File

@@ -356,6 +356,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
Timber.d("onCreate()"); Timber.d("onCreate()");
ThemeHelper.setPreferred(this);
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if (savedInstanceState != null) { if (savedInstanceState != null) {
// activity restarted // activity restarted
@@ -385,7 +386,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
case Toolbar.BUTTON_CLOSE: case Toolbar.BUTTON_CLOSE:
finish(); finish();
break; break;
case Toolbar.BUTTON_CREDITS: case Toolbar.BUTTON_SETTINGS:
Toast.makeText(WalletActivity.this, getString(R.string.label_credits), Toast.LENGTH_SHORT).show(); Toast.makeText(WalletActivity.this, getString(R.string.label_credits), Toast.LENGTH_SHORT).show();
case Toolbar.BUTTON_NONE: case Toolbar.BUTTON_NONE:
default: default:
@@ -505,6 +506,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
Timber.d("onResume()"); Timber.d("onResume()");
} }
@Override
public void saveWallet() { public void saveWallet() {
if (mIsBound) { // no point in talking to unbound service if (mIsBound) { // no point in talking to unbound service
Intent intent = new Intent(getApplicationContext(), WalletService.class); Intent intent = new Intent(getApplicationContext(), WalletService.class);
@@ -537,7 +539,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
@Override @Override
public void onSendRequest(View view) { public void onSendRequest(View view) {
replaceFragmentWithTransition(view, SendFragment.newInstance(uri), null, null); replaceFragment(SendFragment.newInstance(uri), null, null);
uri = null; // only use uri once uri = null; // only use uri once
} }
@@ -609,9 +611,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
@Override @Override
public void onWalletStored(final boolean success) { public void onWalletStored(final boolean success) {
runOnUiThread(() -> { runOnUiThread(() -> {
if (success) { if (!success) {
Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_unloaded), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_unload_failed), Toast.LENGTH_LONG).show(); 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; int transition;
if (newFragment instanceof TxFragment) if (newFragment instanceof TxFragment)
transition = R.string.tx_details_transition_name; 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) else if (newFragment instanceof SubaddressInfoFragment)
transition = R.string.subaddress_info_transition_name; transition = R.string.subaddress_info_transition_name;
else else
@@ -941,6 +937,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
@Override @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Timber.d("onRequestPermissionsResult()"); Timber.d("onRequestPermissionsResult()");
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == Helper.PERMISSIONS_REQUEST_CAMERA) { // If request is cancelled, the result arrays are empty. if (requestCode == Helper.PERMISSIONS_REQUEST_CAMERA) { // If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) { && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
@@ -959,7 +956,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
Bundle b = new Bundle(); Bundle b = new Bundle();
b.putString("address", address); b.putString("address", address);
b.putString("name", getWalletName()); b.putString("name", getWalletName());
replaceFragmentWithTransition(view, new ReceiveFragment(), null, b); replaceFragment(new ReceiveFragment(), null, b);
Timber.d("ReceiveFragment placed"); Timber.d("ReceiveFragment placed");
} }

View File

@@ -43,7 +43,6 @@ import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.github.brnunes.swipeablerecyclerview.SwipeableRecyclerViewTouchListener; import com.github.brnunes.swipeablerecyclerview.SwipeableRecyclerViewTouchListener;
import com.google.android.material.transition.MaterialElevationScale;
import com.m2049r.xmrwallet.layout.TransactionInfoAdapter; import com.m2049r.xmrwallet.layout.TransactionInfoAdapter;
import com.m2049r.xmrwallet.model.TransactionInfo; import com.m2049r.xmrwallet.model.TransactionInfo;
import com.m2049r.xmrwallet.model.Wallet; 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.service.exchange.api.ExchangeRate;
import com.m2049r.xmrwallet.util.Helper; import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.ServiceHelper; import com.m2049r.xmrwallet.util.ServiceHelper;
import com.m2049r.xmrwallet.util.ThemeHelper;
import com.m2049r.xmrwallet.widget.Toolbar; import com.m2049r.xmrwallet.widget.Toolbar;
import java.text.NumberFormat; import java.text.NumberFormat;
@@ -111,7 +111,8 @@ public class WalletFragment extends Fragment
llBalance = view.findViewById(R.id.llBalance); llBalance = view.findViewById(R.id.llBalance);
flExchange = view.findViewById(R.id.flExchange); flExchange = view.findViewById(R.id.flExchange);
((ProgressBar) view.findViewById(R.id.pbExchange)).getIndeterminateDrawable(). ((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); android.graphics.PorterDuff.Mode.MULTIPLY);
tvProgress = view.findViewById(R.id.tvProgress); tvProgress = view.findViewById(R.id.tvProgress);
@@ -202,11 +203,6 @@ public class WalletFragment extends Fragment
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
postponeEnterTransition();
view.getViewTreeObserver().addOnPreDrawListener(() -> {
startPostponedEnterTransition();
return true;
});
} }
void showBalance(String balance) { void showBalance(String balance) {
@@ -331,19 +327,9 @@ public class WalletFragment extends Fragment
// Callbacks from TransactionInfoAdapter // Callbacks from TransactionInfoAdapter
@Override @Override
public void onInteraction(final View view, final TransactionInfo infoItem) { 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); activityCallback.onTxDetailsRequest(view, infoItem);
} }
// called from activity
// if account index has changed scroll to top? // if account index has changed scroll to top?
private int accountIndex = 0; private int accountIndex = 0;
@@ -536,8 +522,6 @@ public class WalletFragment extends Fragment
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
setExitTransition(null);
setReenterTransition(null);
Timber.d("onResume()"); Timber.d("onResume()");
activityCallback.setTitle(walletTitle, walletSubtitle); activityCallback.setTitle(walletTitle, walletSubtitle);
activityCallback.setToolbarButton(Toolbar.BUTTON_NONE); activityCallback.setToolbarButton(Toolbar.BUTTON_NONE);

View File

@@ -22,6 +22,7 @@ import android.content.res.Configuration;
import android.os.Build; import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentManager;
import com.m2049r.xmrwallet.model.NetworkType; import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.util.LocaleHelper; import com.m2049r.xmrwallet.util.LocaleHelper;
@@ -35,7 +36,7 @@ public class XmrWalletApplication extends Application {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
FragmentManager.enableNewStateManager(false);
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree()); Timber.plant(new Timber.DebugTree());
} }

View File

@@ -267,14 +267,14 @@ public class NodeInfo extends Node {
public void showInfo(TextView view, String info, boolean isError) { public void showInfo(TextView view, String info, boolean isError) {
final Context ctx = view.getContext(); final Context ctx = view.getContext();
final Spanned text = Html.fromHtml(ctx.getString(R.string.status, final Spanned text = Html.fromHtml(ctx.getString(R.string.status,
Integer.toHexString(ContextCompat.getColor(ctx, R.color.monerujoGreen) & 0xFFFFFF), Integer.toHexString(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor) & 0xFFFFFF),
Integer.toHexString(ContextCompat.getColor(ctx, R.color.monerujoBackground) & 0xFFFFFF), Integer.toHexString(ThemeHelper.getThemedColor(ctx, android.R.attr.colorBackground) & 0xFFFFFF),
(hostAddress.isOnion() ? "&nbsp;.onion&nbsp;&nbsp;" : ""), " " + info)); (hostAddress.isOnion() ? "&nbsp;.onion&nbsp;&nbsp;" : ""), " " + info));
view.setText(text); view.setText(text);
if (isError) if (isError)
view.setTextColor(ThemeHelper.getThemedColor(ctx, R.attr.colorError)); view.setTextColor(ThemeHelper.getThemedColor(ctx, R.attr.colorError));
else else
view.setTextColor(ThemeHelper.getThemedColor(ctx, R.attr.colorPrimary)); view.setTextColor(ThemeHelper.getThemedColor(ctx, android.R.attr.textColorSecondary));
} }
public void showInfo(TextView view) { public void showInfo(TextView view) {

View File

@@ -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.service.shift.sideshift.network.SideShiftApiImpl;
import com.m2049r.xmrwallet.util.Helper; import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.ServiceHelper; import com.m2049r.xmrwallet.util.ServiceHelper;
import com.m2049r.xmrwallet.util.ThemeHelper;
import java.text.NumberFormat; import java.text.NumberFormat;
import java.util.Locale; import java.util.Locale;
@@ -212,19 +213,27 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
if (status.isError()) { if (status.isError()) {
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_error, status.toString())); tvXmrToStatus.setText(getString(R.string.info_send_xmrto_error, status.toString()));
statusResource = R.drawable.ic_error_red_24dp; 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()) { } else if (status.isSent() || status.isPaid()) {
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_sent, btcData.getBtcSymbol())); tvXmrToStatus.setText(getString(R.string.info_send_xmrto_sent, btcData.getBtcSymbol()));
statusResource = R.drawable.ic_success_green_24dp; statusResource = R.drawable.ic_success;
pbXmrto.getIndeterminateDrawable().setColorFilter(0xFF417505, android.graphics.PorterDuff.Mode.MULTIPLY); pbXmrto.getIndeterminateDrawable().setColorFilter(
ThemeHelper.getThemedColor(getContext(), R.attr.positiveColor),
android.graphics.PorterDuff.Mode.MULTIPLY);
} else if (status.isWaiting()) { } else if (status.isWaiting()) {
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_unpaid)); tvXmrToStatus.setText(getString(R.string.info_send_xmrto_unpaid));
statusResource = R.drawable.ic_pending_orange_24dp; statusResource = R.drawable.ic_pending;
pbXmrto.getIndeterminateDrawable().setColorFilter(0xFFFF6105, android.graphics.PorterDuff.Mode.MULTIPLY); pbXmrto.getIndeterminateDrawable().setColorFilter(
ThemeHelper.getThemedColor(getContext(), R.attr.neutralColor),
android.graphics.PorterDuff.Mode.MULTIPLY);
} else if (status.isPending()) { } else if (status.isPending()) {
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_paid)); tvXmrToStatus.setText(getString(R.string.info_send_xmrto_paid));
statusResource = R.drawable.ic_pending_orange_24dp; statusResource = R.drawable.ic_pending;
pbXmrto.getIndeterminateDrawable().setColorFilter(0xFFFF6105, android.graphics.PorterDuff.Mode.MULTIPLY); pbXmrto.getIndeterminateDrawable().setColorFilter(
ThemeHelper.getThemedColor(getContext(), R.attr.neutralColor),
android.graphics.PorterDuff.Mode.MULTIPLY);
} else { } else {
throw new IllegalStateException("status is broken: " + status.toString()); throw new IllegalStateException("status is broken: " + status.toString());
} }

View File

@@ -55,7 +55,6 @@ 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;
@@ -202,14 +201,14 @@ public class SendFragment extends Fragment
CharSequence nextLabel = pagerAdapter.getPageTitle(position + 1); CharSequence nextLabel = pagerAdapter.getPageTitle(position + 1);
bNext.setText(nextLabel); bNext.setText(nextLabel);
if (nextLabel != null) { 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 { } else {
bNext.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); bNext.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
} }
CharSequence prevLabel = pagerAdapter.getPageTitle(position - 1); CharSequence prevLabel = pagerAdapter.getPageTitle(position - 1);
bPrev.setText(prevLabel); bPrev.setText(prevLabel);
if (prevLabel != null) { 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 { } else {
bPrev.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); bPrev.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
} }

View File

@@ -23,7 +23,6 @@ import android.view.ViewGroup;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
@@ -230,21 +229,21 @@ public class NodeInfoAdapter extends RecyclerView.Adapter<NodeInfoAdapter.ViewHo
static public int getPingIcon(NodeInfo nodeInfo) { static public int getPingIcon(NodeInfo nodeInfo) {
if (nodeInfo.isUnauthorized()) { if (nodeInfo.isUnauthorized()) {
return R.drawable.ic_wifi_lock_black_24dp; return R.drawable.ic_wifi_lock;
} }
if (nodeInfo.isValid()) { if (nodeInfo.isValid()) {
final double ping = nodeInfo.getResponseTime(); final double ping = nodeInfo.getResponseTime();
if (ping < NodeInfo.PING_GOOD) { 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) { } 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) { } else if (ping < NodeInfo.PING_BAD) {
return R.drawable.ic_signal_wifi_2_bar_24dp; return R.drawable.ic_wifi_2_bar;
} else { } else {
return R.drawable.ic_signal_wifi_1_bar_24dp; return R.drawable.ic_wifi_1_bar;
} }
} else { } else {
return R.drawable.ic_signal_wifi_off_24dp; return R.drawable.ic_wifi_off;
} }
} }

View File

@@ -36,6 +36,7 @@ import com.m2049r.xmrwallet.data.Crypto;
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.util.Helper; import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.ThemeHelper;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
@@ -67,10 +68,10 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
public TransactionInfoAdapter(Context context, OnInteractionListener listener) { public TransactionInfoAdapter(Context context, OnInteractionListener listener) {
this.context = context; this.context = context;
inboundColour = ContextCompat.getColor(context, R.color.tx_plus); inboundColour = ThemeHelper.getThemedColor(context, R.attr.positiveColor);
outboundColour = ContextCompat.getColor(context, R.color.tx_minus); outboundColour = ThemeHelper.getThemedColor(context, R.attr.negativeColor);
pendingColour = ContextCompat.getColor(context, R.color.tx_pending); pendingColour = ThemeHelper.getThemedColor(context, R.attr.neutralColor);
failedColour = ContextCompat.getColor(context, R.color.tx_failed); failedColour = ThemeHelper.getThemedColor(context, R.attr.neutralColor);
infoItems = new ArrayList<>(); infoItems = new ArrayList<>();
this.listener = listener; this.listener = listener;
Calendar cal = Calendar.getInstance(); Calendar cal = Calendar.getInstance();
@@ -253,8 +254,8 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
tvPaymentId.setText(info); tvPaymentId.setText(info);
} else { } else {
Spanned label = Html.fromHtml(context.getString(R.string.tx_details_notes, Spanned label = Html.fromHtml(context.getString(R.string.tx_details_notes,
Integer.toHexString(ContextCompat.getColor(context, R.color.monerujoGreen) & 0xFFFFFF), Integer.toHexString(ThemeHelper.getThemedColor(context, R.attr.positiveColor) & 0xFFFFFF),
Integer.toHexString(ContextCompat.getColor(context, R.color.monerujoBackground) & 0xFFFFFF), Integer.toHexString(ThemeHelper.getThemedColor(context, android.R.attr.colorBackground) & 0xFFFFFF),
tag, info.isEmpty() ? "" : ("&nbsp; " + info))); tag, info.isEmpty() ? "" : ("&nbsp; " + info)));
tvPaymentId.setText(label); tvPaymentId.setText(label);
} }

View File

@@ -129,7 +129,7 @@ public class Wallet {
ConnectionStatus_WrongVersion ConnectionStatus_WrongVersion
} }
public native String getSeed(); public native String getSeed(String offset);
public native String getSeedLanguage(); public native String getSeedLanguage();
@@ -147,7 +147,7 @@ public class Wallet {
private native Status statusWithErrorString(); private native Status statusWithErrorString();
public native boolean setPassword(String password); public native synchronized boolean setPassword(String password);
public String getAddress() { public String getAddress() {
return getAddress(accountIndex); return getAddress(accountIndex);
@@ -203,12 +203,10 @@ public class Wallet {
public native String getSecretSpendKey(); public native String getSecretSpendKey();
public boolean store() { public boolean store() {
final boolean ok = store(""); return store("");
Timber.d("stored");
return ok;
} }
public native boolean store(String path); public native synchronized boolean store(String path);
public boolean close() { public boolean close() {
disposePendingTransaction(); disposePendingTransaction();

View File

@@ -124,19 +124,19 @@ public class WalletManager {
private native long openWalletJ(String path, String password, int networkType); private native long openWalletJ(String path, String password, int networkType);
public Wallet recoveryWallet(File aFile, String password, String mnemonic) { public Wallet recoveryWallet(File aFile, String password,
return recoveryWallet(aFile, password, mnemonic, 0); String mnemonic, String offset,
} long restoreHeight) {
long walletHandle = recoveryWalletJ(aFile.getAbsolutePath(), password,
public Wallet recoveryWallet(File aFile, String password, String mnemonic, long restoreHeight) { mnemonic, offset,
long walletHandle = recoveryWalletJ(aFile.getAbsolutePath(), password, mnemonic,
getNetworkType().getValue(), restoreHeight); getNetworkType().getValue(), restoreHeight);
Wallet wallet = new Wallet(walletHandle); Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet); manageWallet(wallet);
return 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); int networkType, long restoreHeight);
public Wallet createWalletWithKeys(File aFile, String password, String language, long restoreHeight, public Wallet createWalletWithKeys(File aFile, String password, String language, long restoreHeight,

View File

@@ -568,7 +568,8 @@ public class WalletService extends Service {
private void startNotfication() { private void startNotfication() {
Intent notificationIntent = new Intent(this, WalletActivity.class); 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() : ""; String channelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? createNotificationChannel() : "";
Notification notification = new NotificationCompat.Builder(this, channelId) Notification notification = new NotificationCompat.Builder(this, channelId)

View File

@@ -395,7 +395,7 @@ public class Helper {
final TextView tvOpenPrompt = promptsView.findViewById(R.id.tvOpenPrompt); final TextView tvOpenPrompt = promptsView.findViewById(R.id.tvOpenPrompt);
final Drawable icFingerprint = context.getDrawable(R.drawable.ic_fingerprint); final Drawable icFingerprint = context.getDrawable(R.drawable.ic_fingerprint);
final Drawable icError = context.getDrawable(R.drawable.ic_error_red_36dp); 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); final boolean fingerprintAuthCheck = FingerprintHelper.isFingerPassValid(context, wallet);

View File

@@ -27,7 +27,6 @@ import java.util.ArrayList;
import java.util.Locale; import java.util.Locale;
public class LocaleHelper { public class LocaleHelper {
private static final String PREFERRED_LOCALE_KEY = "preferred_locale";
private static Locale SYSTEM_DEFAULT_LOCALE = Locale.getDefault(); private static Locale SYSTEM_DEFAULT_LOCALE = Locale.getDefault();
public static ArrayList<Locale> getAvailableLocales(Context context) { public static ArrayList<Locale> getAvailableLocales(Context context) {
@@ -93,12 +92,13 @@ public class LocaleHelper {
public static String getPreferredLanguageTag(Context context) { public static String getPreferredLanguageTag(Context context) {
return PreferenceManager.getDefaultSharedPreferences(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") @SuppressLint("ApplySharedPref")
private static void savePreferredLangaugeTag(Context context, String locale) { private static void savePreferredLangaugeTag(Context context, String locale) {
PreferenceManager.getDefaultSharedPreferences(context).edit() PreferenceManager.getDefaultSharedPreferences(context).edit()
.putString(PREFERRED_LOCALE_KEY, locale).commit(); .putString(context.getString(R.string.preferred_locale), locale).commit();
} }
} }

View File

@@ -323,6 +323,7 @@ public class NetCipherHelper implements StatusCallback {
} }
private OkHttpClient getClient() { private OkHttpClient getClient() {
if (mockClient != null) return mockClient; // Unit-test mode
final OkHttpClient client = getInstance().client; final OkHttpClient client = getInstance().client;
if ((username != null) && (!username.isEmpty())) { if ((username != null) && (!username.isEmpty())) {
final DigestAuthenticator authenticator = new DigestAuthenticator(new Credentials(username, password)); final DigestAuthenticator authenticator = new DigestAuthenticator(new Credentials(username, password));
@@ -349,6 +350,9 @@ public class NetCipherHelper implements StatusCallback {
} }
return builder.build(); return builder.build();
} }
// for unit tests only
static public OkHttpClient mockClient = null;
} }
private static final String PREFS_NAME = "tor"; private static final String PREFS_NAME = "tor";

View File

@@ -16,32 +16,37 @@
package com.m2049r.xmrwallet.util; package com.m2049r.xmrwallet.util;
import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.app.AppCompatDelegate;
public class NightmodeHelper { import com.m2049r.xmrwallet.R;
private static final String PREFERRED_NIGHTMODE_KEY = "preferred_nightmode";
public class NightmodeHelper {
public static DayNightMode getPreferredNightmode(Context context) { public static DayNightMode getPreferredNightmode(Context context) {
return DayNightMode.valueOf(PreferenceManager.getDefaultSharedPreferences(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) { public static void setPreferredNightmode(Context context) {
final DayNightMode mode = DayNightMode.valueOf(PreferenceManager.getDefaultSharedPreferences(context) final DayNightMode mode = DayNightMode.valueOf(PreferenceManager.getDefaultSharedPreferences(context)
.getString(PREFERRED_NIGHTMODE_KEY, "UNKNOWN")); .getString(context.getString(R.string.preferred_nightmode), "UNKNOWN"));
if (mode == DayNightMode.UNKNOWN) setAndSavePreferredNightmode(context, DayNightMode.AUTO); if (mode == DayNightMode.UNKNOWN)
setNightMode(mode); setAndSavePreferredNightmode(context, DayNightMode.AUTO);
else
setNightMode(mode);
} }
public static void setAndSavePreferredNightmode(Context context, DayNightMode mode) { public static void setAndSavePreferredNightmode(Context context, DayNightMode mode) {
PreferenceManager.getDefaultSharedPreferences(context).edit() PreferenceManager.getDefaultSharedPreferences(context).edit()
.putString(PREFERRED_NIGHTMODE_KEY, mode.name()).apply(); .putString(context.getString(R.string.preferred_nightmode), mode.name()).apply();
setNightMode(mode); setNightMode(mode);
} }
@SuppressLint("WrongConstant")
public static void setNightMode(DayNightMode mode) { public static void setNightMode(DayNightMode mode) {
AppCompatDelegate.setDefaultNightMode(mode.getNightMode()); AppCompatDelegate.setDefaultNightMode(mode.getNightMode());
} }

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