Fee selection and code cleanup

This commit is contained in:
pokkst 2022-09-19 12:47:52 -05:00
parent 7ca22077d6
commit 5b9b1f0a8f
No known key found for this signature in database
GPG Key ID: 90C2ED85E67A50FF
7 changed files with 96 additions and 300 deletions

View File

@ -134,7 +134,7 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'androidx.preference:preference:1.2.0'
implementation 'com.google.android.material:material:1.6.0'
implementation 'com.google.android.material:material:1.6.1'
implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
implementation "com.squareup.okhttp3:okhttp:4.9.3"

View File

@ -10,6 +10,7 @@ import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;
@ -58,13 +59,16 @@ public class SendBottomSheetDialog extends BottomSheetDialogFragment {
private TextView feeTextView;
private TextView addressTextView;
private TextView amountTextView;
private TextView feeRadioGroupLabelTextView;
private Button createButton;
private Button sendButton;
private Button sendMaxButton;
private ImageButton pasteAddressImageButton;
private ImageButton scanAddressImageButton;
private RadioGroup feeRadioGroup;
public UriData uriData = null;
public PendingTransaction.Priority priority;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@ -85,6 +89,8 @@ public class SendBottomSheetDialog extends BottomSheetDialogFragment {
feeTextView = view.findViewById(R.id.fee_textview);
addressTextView = view.findViewById(R.id.address_pending_textview);
amountTextView = view.findViewById(R.id.amount_pending_textview);
feeRadioGroup = view.findViewById(R.id.tx_fee_radiogroup);
feeRadioGroupLabelTextView = view.findViewById(R.id.tx_fee_radiogroup_label_textview);
if (uriData != null) {
addressEditText.setText(uriData.getAddress());
@ -93,6 +99,18 @@ public class SendBottomSheetDialog extends BottomSheetDialogFragment {
}
}
feeRadioGroup.check(R.id.low_fee_radiobutton);
priority = PendingTransaction.Priority.Priority_Low;
feeRadioGroup.setOnCheckedChangeListener((radioGroup, i) -> {
if(i == R.id.low_fee_radiobutton) {
priority = PendingTransaction.Priority.Priority_Low;
} else if(i == R.id.med_fee_radiobutton) {
priority = PendingTransaction.Priority.Priority_Medium;
} else if(i == R.id.high_fee_radiobutton) {
priority = PendingTransaction.Priority.Priority_High;
}
});
pasteAddressImageButton.setOnClickListener(view1 -> {
Context ctx = getContext();
if (ctx != null) {
@ -228,6 +246,8 @@ public class SendBottomSheetDialog extends BottomSheetDialogFragment {
feeTextView.setVisibility(View.VISIBLE);
addressTextView.setVisibility(View.VISIBLE);
amountTextView.setVisibility(View.VISIBLE);
feeRadioGroup.setVisibility(View.GONE);
feeRadioGroupLabelTextView.setVisibility(View.GONE);
} else {
sendButton.setVisibility(View.GONE);
addressEditText.setVisibility(View.VISIBLE);
@ -240,6 +260,8 @@ public class SendBottomSheetDialog extends BottomSheetDialogFragment {
feeTextView.setVisibility(View.GONE);
addressTextView.setVisibility(View.GONE);
amountTextView.setVisibility(View.GONE);
feeRadioGroup.setVisibility(View.VISIBLE);
feeRadioGroupLabelTextView.setVisibility(View.VISIBLE);
}
}

View File

@ -1,168 +0,0 @@
package net.mynero.wallet.util;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.preference.PreferenceManager;
import net.mynero.wallet.BuildConfig;
import net.mynero.wallet.model.WalletManager;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.RequiredArgsConstructor;
import timber.log.Timber;
@RequiredArgsConstructor
public class LegacyStorageHelper {
private static final Pattern WALLET_PATTERN = Pattern.compile("^(.+) \\(([0-9]+)\\).keys$");
private static final String MIGRATED_KEY = "migrated_legacy_storage";
final private File srcDir;
final private File dstDir;
static public void migrateWallets(Context context) {
try {
if (isStorageMigrated(context)) return;
if (!hasReadPermission(context)) {
// can't migrate - don't remember this, as the user may turn on permissions later
return;
}
final File oldRoot = getWalletRoot();
if (!oldRoot.exists()) {
// nothing to migrate, so don't try again
setStorageMigrated(context);
return;
}
final File newRoot = Helper.getWalletRoot(context);
(new LegacyStorageHelper(oldRoot, newRoot)).migrate();
setStorageMigrated(context); // done it once - don't try again
} catch (IllegalStateException ex) {
Timber.d(ex);
// nothing we can do here
}
}
private static boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
return Environment.MEDIA_MOUNTED.equals(state);
}
private static File getWalletRoot() {
if (!isExternalStorageWritable())
throw new IllegalStateException();
// wallet folder for legacy (pre-Q) installations
final String FLAVOR_SUFFIX =
(BuildConfig.FLAVOR.startsWith("prod") ? "" : "." + BuildConfig.FLAVOR)
+ (BuildConfig.DEBUG ? "-debug" : "");
final String WALLET_DIR = "monerujo" + FLAVOR_SUFFIX;
File dir = new File(Environment.getExternalStorageDirectory(), WALLET_DIR);
if (!dir.exists() || !dir.isDirectory())
throw new IllegalStateException();
return dir;
}
private static boolean hasReadPermission(Context context) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
return context.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_DENIED;
} else {
return true;
}
}
private static String getUniqueName(File root, String name) {
if (!(new File(root, name + ".keys")).exists()) // <name> does not exist => it's ok to use
return name;
File[] wallets = root.listFiles(
(dir, filename) -> {
Matcher m = WALLET_PATTERN.matcher(filename);
if (m.find())
return m.group(1).equals(name);
else return false;
});
if (wallets.length == 0) return name + " (1)";
int maxIndex = 0;
for (File wallet : wallets) {
try {
final Matcher m = WALLET_PATTERN.matcher(wallet.getName());
if (!m.find())
throw new IllegalStateException("this must match as it did before");
final int index = Integer.parseInt(m.group(2));
if (index > maxIndex) maxIndex = index;
} catch (NumberFormatException ex) {
// this cannot happen & we can ignore it if it does
}
}
return name + " (" + (maxIndex + 1) + ")";
}
public static boolean isStorageMigrated(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(MIGRATED_KEY, false);
}
public static void setStorageMigrated(Context context) {
PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(MIGRATED_KEY, true).apply();
}
public void migrate() {
String addressPrefix = WalletManager.getInstance().addressPrefix();
File[] wallets = srcDir.listFiles((dir, filename) -> filename.endsWith(".keys"));
if (wallets == null) return;
for (File wallet : wallets) {
final String walletName = wallet.getName().substring(0, wallet.getName().length() - ".keys".length());
if (addressPrefix.indexOf(getAddress(walletName).charAt(0)) < 0) {
Timber.d("skipping %s", walletName);
continue;
}
try {
copy(walletName);
} catch (IOException ex) { // something failed - try to clean up
deleteDst(walletName);
}
}
}
// return "@" by default so we don't need to deal with null stuff
private String getAddress(String walletName) {
File addressFile = new File(srcDir, walletName + ".address.txt");
if (!addressFile.exists()) return "@";
try (BufferedReader addressReader = new BufferedReader(new FileReader(addressFile))) {
return addressReader.readLine();
} catch (IOException ex) {
Timber.d(ex.getLocalizedMessage());
}
return "@";
}
private void copy(String walletName) throws IOException {
final String dstName = getUniqueName(dstDir, walletName);
copyFile(new File(srcDir, walletName), new File(dstDir, dstName));
copyFile(new File(srcDir, walletName + ".keys"), new File(dstDir, dstName + ".keys"));
}
private void deleteDst(String walletName) {
// do our best, but if it fails, it fails
(new File(dstDir, walletName)).delete();
(new File(dstDir, walletName + ".keys")).delete();
}
private void copyFile(File src, File dst) throws IOException {
if (!src.exists()) return;
Timber.d("%s => %s", src.getAbsolutePath(), dst.getAbsolutePath());
try (FileChannel inChannel = new FileInputStream(src).getChannel();
FileChannel outChannel = new FileOutputStream(dst).getChannel()) {
inChannel.transferTo(0, inChannel.size(), outChannel);
}
}
}

View File

@ -1,104 +0,0 @@
/*
* Copyright (c) 2018-2020 m2049r et al.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.mynero.wallet.util;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Configuration;
import android.preference.PreferenceManager;
import net.mynero.wallet.R;
import java.util.ArrayList;
import java.util.Locale;
public class LocaleHelper {
private static Locale SYSTEM_DEFAULT_LOCALE = Locale.getDefault();
public static ArrayList<Locale> getAvailableLocales(Context context) {
ArrayList<Locale> locales = new ArrayList<>();
// R.string.available_locales gets generated in build.gradle by enumerating values-* folders
String[] availableLocales = context.getString(R.string.available_locales).split(",");
for (String localeName : availableLocales) {
locales.add(Locale.forLanguageTag(localeName));
}
return locales;
}
public static String getDisplayName(Locale locale, boolean sentenceCase) {
String displayName = locale.getDisplayName(locale);
if (sentenceCase) {
displayName = toSentenceCase(displayName, locale);
}
return displayName;
}
public static Context setPreferredLocale(Context context) {
return setLocale(context, getPreferredLanguageTag(context));
}
public static Context setAndSaveLocale(Context context, String langaugeTag) {
savePreferredLangaugeTag(context, langaugeTag);
return setLocale(context, langaugeTag);
}
private static Context setLocale(Context context, String languageTag) {
Locale locale = (languageTag.isEmpty()) ? SYSTEM_DEFAULT_LOCALE : Locale.forLanguageTag(languageTag);
Locale.setDefault(locale);
Configuration configuration = context.getResources().getConfiguration();
configuration.setLocale(locale);
configuration.setLayoutDirection(locale);
return context.createConfigurationContext(configuration);
}
public static void updateSystemDefaultLocale(Locale locale) {
SYSTEM_DEFAULT_LOCALE = locale;
}
private static String toSentenceCase(String str, Locale locale) {
if (str.isEmpty()) {
return str;
}
int firstCodePointLen = str.offsetByCodePoints(0, 1);
return str.substring(0, firstCodePointLen).toUpperCase(locale)
+ str.substring(firstCodePointLen);
}
public static Locale getPreferredLocale(Context context) {
String languageTag = getPreferredLanguageTag(context);
return languageTag.isEmpty() ? SYSTEM_DEFAULT_LOCALE : Locale.forLanguageTag(languageTag);
}
public static String getPreferredLanguageTag(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context)
.getString("preferred_locale", "");
// cannot access getString here as it's done BEFORE string locale is set
}
@SuppressLint("ApplySharedPref")
private static void savePreferredLangaugeTag(Context context, String locale) {
PreferenceManager.getDefaultSharedPreferences(context).edit()
.putString(context.getString(R.string.preferred_locale), locale).commit();
}
}

View File

@ -17,7 +17,7 @@
android:id="@+id/send_monero_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:layout_marginBottom="16dp"
android:text="@string/send_monero"
android:textSize="32sp"
android:textStyle="bold"
@ -30,7 +30,7 @@
android:id="@+id/address_edittext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:layout_marginBottom="16dp"
android:background="@drawable/edittext_bg"
android:ellipsize="middle"
android:hint="@string/address"
@ -39,7 +39,7 @@
app:layout_constraintEnd_toStartOf="@id/paste_address_imagebutton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/send_monero_textview"
tools:visibility="gone" />
tools:visibility="visible" />
<ImageButton
android:id="@+id/paste_address_imagebutton"
@ -48,8 +48,8 @@
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:background="@android:color/transparent"
android:minWidth="24dp"
android:minHeight="24dp"
android:minWidth="48dp"
android:minHeight="48dp"
android:padding="8dp"
android:src="@drawable/ic_content_paste_24dp"
app:layout_constraintBottom_toBottomOf="@id/address_edittext"
@ -57,15 +57,15 @@
app:layout_constraintStart_toEndOf="@id/address_edittext"
app:layout_constraintTop_toTopOf="@id/address_edittext"
tools:ignore="SpeakableTextPresentCheck"
tools:visibility="gone" />
tools:visibility="visible" />
<ImageButton
android:id="@+id/scan_address_imagebutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:minWidth="24dp"
android:minHeight="24dp"
android:minWidth="48dp"
android:minHeight="48dp"
android:padding="8dp"
android:src="@drawable/ic_scan"
app:layout_constraintBottom_toBottomOf="@id/address_edittext"
@ -73,20 +73,21 @@
app:layout_constraintStart_toEndOf="@id/paste_address_imagebutton"
app:layout_constraintTop_toTopOf="@id/address_edittext"
tools:ignore="SpeakableTextPresentCheck"
tools:visibility="gone" />
tools:visibility="visible" />
<EditText
android:id="@+id/amount_edittext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="16dp"
android:background="@drawable/edittext_bg"
android:hint="@string/amount"
android:inputType="numberDecimal"
app:layout_constraintBottom_toTopOf="@id/create_tx_button"
app:layout_constraintBottom_toTopOf="@id/tx_fee_radiogroup"
app:layout_constraintEnd_toStartOf="@id/send_max_button"
app:layout_constraintStart_toStartOf="parent"
tools:visibility="gone" />
tools:visibility="visible" />
<TextView
android:id="@+id/sending_all_textview"
@ -101,7 +102,7 @@
app:layout_constraintEnd_toStartOf="@id/send_max_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/amount_edittext"
tools:visibility="gone" />
tools:visibility="visible" />
<Button
android:id="@+id/send_max_button"
@ -113,18 +114,62 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/amount_edittext"
app:layout_constraintTop_toTopOf="@id/amount_edittext"
tools:visibility="gone" />
tools:visibility="visible" />
<TextView
android:id="@+id/tx_fee_radiogroup_label_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/fee_priority"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/tx_fee_radiogroup"
app:layout_constraintTop_toTopOf="@id/tx_fee_radiogroup"
app:layout_constraintBottom_toBottomOf="@id/tx_fee_radiogroup"/>
<RadioGroup
android:id="@+id/tx_fee_radiogroup"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="@id/send_max_button"
app:layout_constraintBottom_toTopOf="@id/create_tx_button"
app:layout_constraintStart_toEndOf="@id/tx_fee_radiogroup_label_textview"
app:layout_constraintEnd_toEndOf="parent">
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/low"
android:id="@+id/low_fee_radiobutton"
android:checked="true"
android:textSize="16sp" />
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/medium"
android:id="@+id/med_fee_radiobutton"
android:checked="false"
android:textSize="16sp"/>
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/high"
android:id="@+id/high_fee_radiobutton"
android:checked="false"
android:textSize="16sp"/>
</RadioGroup>
<Button
android:id="@+id/create_tx_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:layout_marginTop="16dp"
android:background="@drawable/button_bg"
android:text="@string/create"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/amount_edittext"
tools:visibility="gone" />
app:layout_constraintTop_toBottomOf="@id/tx_fee_radiogroup"
tools:visibility="visible" />
<!-- SEND LAYOUT -->
@ -132,7 +177,7 @@
android:id="@+id/address_pending_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:layout_marginTop="16dp"
android:ellipsize="middle"
android:singleLine="true"
android:text="@string/tx_address_text"
@ -143,13 +188,13 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/send_monero_textview"
tools:visibility="visible" />
tools:visibility="gone" />
<TextView
android:id="@+id/amount_pending_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:layout_marginTop="16dp"
android:text="@string/tx_amount_text"
android:textSize="16sp"
android:textStyle="bold"
@ -158,13 +203,13 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/address_pending_textview"
tools:visibility="visible" />
tools:visibility="gone" />
<TextView
android:id="@+id/fee_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:layout_marginTop="16dp"
android:text="@string/tx_fee_text"
android:textSize="16sp"
android:textStyle="bold"
@ -173,19 +218,19 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/amount_pending_textview"
tools:visibility="visible" />
tools:visibility="gone" />
<Button
android:id="@+id/send_tx_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:layout_marginTop="16dp"
android:background="@drawable/button_bg"
android:text="@string/send"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/fee_textview"
tools:visibility="visible" />
tools:visibility="gone" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View File

@ -97,4 +97,8 @@
<string name="confirmations">Confirmations</string>
<string name="date">Date</string>
<string name="node_selected">Node has been selected</string>
<string name="fee_priority">Fee priority:</string>
<string name="low">Low</string>
<string name="medium">Medium</string>
<string name="high">High</string>
</resources>

View File

@ -1,3 +0,0 @@
<resources>
</resources>