Add seed type toggle to onboarding screen

This commit is contained in:
pokkst 2023-12-05 11:13:20 -06:00
parent fc853dc55d
commit 5893c52c27
No known key found for this signature in database
GPG key ID: EC4FAAA66859FAA4
4 changed files with 125 additions and 34 deletions

View file

@ -94,6 +94,9 @@ public class OnboardingFragment extends Fragment implements NodeSelectionBottomS
private Button selectNodeButton;
private SwitchCompat showXmrchanSwitch;
private ImageView xmrchanOnboardingImage;
private TextView seedTypeLabelTextView;
private TextView seedTypeTextView;
private TextView seedTypeDescTextView;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@ -122,6 +125,9 @@ public class OnboardingFragment extends Fragment implements NodeSelectionBottomS
advancedOptionsLayout = view.findViewById(R.id.more_options_layout);
showXmrchanSwitch = view.findViewById(R.id.show_xmrchan_switch);
xmrchanOnboardingImage = view.findViewById(R.id.xmrchan_onboarding_imageview);
seedTypeLabelTextView = view.findViewById(R.id.seed_type_label_textview);
seedTypeTextView = view.findViewById(R.id.seed_type_name_textview);
seedTypeDescTextView = view.findViewById(R.id.seed_type_desc_textview);
bindListeners();
bindObservers();
@ -141,6 +147,20 @@ public class OnboardingFragment extends Fragment implements NodeSelectionBottomS
mViewModel.enableCreateButton.observe(getViewLifecycleOwner(), enable -> {
createWalletButton.setEnabled(enable);
});
mViewModel.seedType.observe(getViewLifecycleOwner(), seedType -> {
seedTypeTextView.setText(seedType.toString());
seedTypeDescTextView.setText(getText(seedType.getDescResId()));
if(seedType == OnboardingViewModel.SeedType.LEGACY) {
seedOffsetCheckbox.setVisibility(View.VISIBLE);
walletRestoreHeightEditText.setVisibility(View.VISIBLE);
walletPasswordEditText.setHint(getString(R.string.password_optional));
} else {
seedOffsetCheckbox.setVisibility(View.GONE);
walletRestoreHeightEditText.setVisibility(View.GONE);
walletPasswordEditText.setHint(getString(R.string.password_non_optional));
}
});
}
private void bindListeners() {
@ -198,16 +218,6 @@ public class OnboardingFragment extends Fragment implements NodeSelectionBottomS
@Override
public void afterTextChanged(Editable editable) {
String text = editable.toString();
OnboardingViewModel.SeedType seedType = mViewModel.getMnemonicType(text);
if(seedType == OnboardingViewModel.SeedType.LEGACY) {
seedOffsetCheckbox.setVisibility(View.VISIBLE);
walletRestoreHeightEditText.setVisibility(View.VISIBLE);
walletPasswordEditText.setHint(getString(R.string.password_optional));
} else {
seedOffsetCheckbox.setVisibility(View.GONE);
walletRestoreHeightEditText.setVisibility(View.GONE);
walletPasswordEditText.setHint(getString(R.string.password_non_optional));
}
if (text.isEmpty()) {
createWalletButton.setText(R.string.create_wallet);
@ -216,6 +226,11 @@ public class OnboardingFragment extends Fragment implements NodeSelectionBottomS
}
}
});
seedTypeLabelTextView.setOnClickListener(v -> toggleSeedType());
seedTypeTextView.setOnClickListener(v -> toggleSeedType());
seedTypeDescTextView.setOnClickListener(v -> toggleSeedType());
torSwitch.setOnCheckedChangeListener((compoundButton, b) -> {
PrefService.getInstance().edit().putBoolean(Constants.PREF_USES_TOR, b).apply();
if (b) {
@ -257,6 +272,19 @@ public class OnboardingFragment extends Fragment implements NodeSelectionBottomS
});
}
private void toggleSeedType() {
OnboardingViewModel.SeedType seedType = mViewModel.seedType.getValue();
if(seedType == null) return;
OnboardingViewModel.SeedType newSeedType = OnboardingViewModel.SeedType.UNKNOWN;
if(seedType == OnboardingViewModel.SeedType.POLYSEED) {
newSeedType = OnboardingViewModel.SeedType.LEGACY;
} else if(seedType == OnboardingViewModel.SeedType.LEGACY) {
newSeedType = OnboardingViewModel.SeedType.POLYSEED;
}
mViewModel.setSeedType(newSeedType);
}
private void prepareDefaultNode() {
PrefService.getInstance().getNode();
}

View file

@ -25,6 +25,8 @@ public class OnboardingViewModel extends ViewModel {
public LiveData<Boolean> showMoreOptions = _showMoreOptions;
private final MutableLiveData<Boolean> _enableCreateButton = new MutableLiveData<>(true);
public LiveData<Boolean> enableCreateButton = _enableCreateButton;
private final MutableLiveData<SeedType> _seedType = new MutableLiveData<>(SeedType.POLYSEED);
public LiveData<SeedType> seedType = _seedType;
private String proxyAddress = "";
private String proxyPort = "";
@ -53,6 +55,10 @@ public class OnboardingViewModel extends ViewModel {
});
}
public void setSeedType(SeedType seedType) {
this._seedType.setValue(seedType);
}
public void setProxyAddress(String address) {
this.proxyAddress = address;
@ -65,13 +71,11 @@ public class OnboardingViewModel extends ViewModel {
public void createOrImportWallet(Activity mainActivity, String walletPassword, String confirmedPassword, String walletSeed, String restoreHeightText, boolean useOffset) {
MoneroApplication application = (MoneroApplication)mainActivity.getApplication();
application.getExecutor().execute(() -> {
mainActivity.runOnUiThread(() -> {
_enableCreateButton.setValue(false);
});
_enableCreateButton.postValue(false);
String offset = useOffset ? walletPassword : "";
if (!walletPassword.isEmpty()) {
if(!walletPassword.equals(confirmedPassword)) {
_enableCreateButton.setValue(true);
_enableCreateButton.postValue(true);
mainActivity.runOnUiThread(() -> Toast.makeText(mainActivity, application.getString(R.string.invalid_confirmed_password), Toast.LENGTH_SHORT).show());
return;
}
@ -85,19 +89,29 @@ public class OnboardingViewModel extends ViewModel {
}
if (walletSeed.isEmpty()) {
if(offset.isEmpty()) {
mainActivity.runOnUiThread(() -> {
_enableCreateButton.setValue(true);
Toast.makeText(mainActivity, application.getString(R.string.invalid_empty_passphrase), Toast.LENGTH_SHORT).show();
});
return;
} else {
wallet = WalletManager.getInstance().createWalletPolyseed(walletFile, walletPassword, offset, Constants.MNEMONIC_LANGUAGE);
SeedType seedTypeValue = seedType.getValue();
if(seedTypeValue == null) return;
if(seedTypeValue == SeedType.POLYSEED) {
if(offset.isEmpty()) {
mainActivity.runOnUiThread(() -> {
_enableCreateButton.postValue(true);
Toast.makeText(mainActivity, application.getString(R.string.invalid_empty_passphrase), Toast.LENGTH_SHORT).show();
});
return;
} else {
wallet = WalletManager.getInstance().createWalletPolyseed(walletFile, walletPassword, offset, Constants.MNEMONIC_LANGUAGE);
}
} else if(seedTypeValue == SeedType.LEGACY) {
File tmpWalletFile = new File(mainActivity.getApplicationInfo().dataDir, Constants.WALLET_NAME + "_tmp");
Wallet tmpWallet = createTempWallet(tmpWalletFile); //we do this to get seed, then recover wallet so we can use seed offset
wallet = WalletManager.getInstance().recoveryWallet(walletFile, walletPassword, tmpWallet.getSeed(""), offset, restoreHeight);
tmpWalletFile.delete();
}
} else {
if (getMnemonicType(walletSeed) == SeedType.UNKNOWN) {
mainActivity.runOnUiThread(() -> {
_enableCreateButton.setValue(true);
_enableCreateButton.postValue(true);
Toast.makeText(mainActivity, application.getString(R.string.invalid_mnemonic_code), Toast.LENGTH_SHORT).show();
});
return;
@ -110,14 +124,14 @@ public class OnboardingViewModel extends ViewModel {
Wallet.Status walletStatus = wallet.getStatus();
wallet.close();
boolean ok = walletStatus.isOk();
//walletFile.delete(); // cache is broken for some reason when recovering wallets. delete the file here. this happens in monerujo too.
walletFile.delete(); // cache is broken for some reason when recovering wallets. delete the file here. this happens in monerujo too.
if (ok) {
((MainActivity)mainActivity).init(walletFile, walletPassword);
mainActivity.runOnUiThread(mainActivity::onBackPressed);
} else {
mainActivity.runOnUiThread(() -> {
_enableCreateButton.setValue(true);
_enableCreateButton.postValue(true);
Toast.makeText(mainActivity, application.getString(R.string.create_wallet_failed, walletStatus.getErrorString()), Toast.LENGTH_SHORT).show();
});
}
@ -132,18 +146,34 @@ public class OnboardingViewModel extends ViewModel {
public SeedType getMnemonicType(String seed) {
String[] words = seed.split("\\s");
if(words.length == 16) {
SeedType seedTypeValue = seedType.getValue();
if(seedTypeValue == null) return SeedType.LEGACY;
if(words.length == 16 && seedTypeValue == SeedType.POLYSEED) {
return SeedType.POLYSEED;
} else if (words.length == 25){
} else if (words.length == 25 && seedTypeValue == SeedType.LEGACY){
return SeedType.LEGACY;
} else {
return SeedType.UNKNOWN;
}
}
private Wallet createTempWallet(File tmpWalletFile) {
return WalletManager.getInstance().createWallet(tmpWalletFile, "", Constants.MNEMONIC_LANGUAGE, 0);
}
public enum SeedType {
LEGACY,
POLYSEED,
UNKNOWN
LEGACY(R.string.seed_desc_legacy),
POLYSEED(R.string.seed_desc_polyseed),
UNKNOWN(0);
private int descResId;
SeedType(int descResId) {
this.descResId = descResId;
}
public int getDescResId() {
return descResId;
}
}
}

View file

@ -7,8 +7,9 @@
<ImageView
android:id="@+id/xmrchan_onboarding_imageview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_height="match_parent"
android:scaleType="fitEnd"
android:adjustViewBounds="false"
android:src="@drawable/xmrchan_half"
app:layout_constraintBottom_toBottomOf="parent"/>
<ScrollView
@ -67,11 +68,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:layout_marginTop="16dp"
android:text="@string/more_options"
android:textStyle="bold"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:textSize="18sp"
app:layout_constraintBottom_toTopOf="@id/more_options_layout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
@ -132,10 +134,39 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@id/wallet_seed_edittext"
app:layout_constraintBottom_toTopOf="@id/seed_type_label_textview"
app:layout_constraintTop_toBottomOf="@id/show_xmrchan_switch"
tools:ignore="SpeakableTextPresentCheck" />
<TextView
android:id="@+id/seed_type_label_textview"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Seed version"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/seed_type_name_textview"
app:layout_constraintTop_toBottomOf="@id/select_node_button"
app:layout_constraintBottom_toTopOf="@id/seed_type_name_textview"/>
<TextView
android:id="@+id/seed_type_name_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="POLYSEED"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/seed_type_label_textview"
app:layout_constraintBottom_toBottomOf="@id/seed_type_label_textview"/>
<TextView
android:id="@+id/seed_type_desc_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/oled_addressListColor"
android:paddingBottom="16dp"
android:paddingTop="8dp"
android:text="16 words instead of 25; just as secure."
app:layout_constraintTop_toBottomOf="@id/seed_type_label_textview"
app:layout_constraintBottom_toTopOf="@id/wallet_seed_edittext"/>
<EditText
android:id="@+id/wallet_seed_edittext"
android:layout_width="0dp"
@ -147,7 +178,7 @@
app:layout_constraintBottom_toTopOf="@id/wallet_restore_height_edittext"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/select_node_button" />
app:layout_constraintTop_toBottomOf="@id/seed_type_desc_textview" />
<EditText
android:id="@+id/wallet_restore_height_edittext"

View file

@ -153,5 +153,7 @@
<string name="scan_qr_code_for_address_field">Scan QR code for address field</string>
<string name="copy_transaction_hash">Copy transaction hash</string>
<string name="copy_transaction_addr">Copy transaction address</string>
<string name="seed_desc_polyseed">16 words instead of 25; just as secure, but not supported in as many wallets right now. In Mysu, seed passphrase is enforced for these wallets.</string>
<string name="seed_desc_legacy">Older, 25 word seed; supported in all Monero wallets. In Mysu, seed passphrase is not enforced for these wallets.</string>
</resources>