separate wallet generation & confirmation

all methods of recovery & creation implemented
This commit is contained in:
m2049r 2017-08-16 19:39:53 +02:00
parent 952fb3a7f1
commit bd598deddd
12 changed files with 747 additions and 182 deletions

View File

@ -37,7 +37,7 @@
<ConfirmationsSetting value="0" id="Add" /> <ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" /> <ConfirmationsSetting value="0" id="Remove" />
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

View File

@ -1,3 +1,19 @@
/*
* Copyright (c) 2017 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet; package com.m2049r.xmrwallet;
import android.app.Fragment; import android.app.Fragment;
@ -5,6 +21,7 @@ import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -24,15 +41,17 @@ import java.io.File;
// TODO: somehow show which net we are generating for // TODO: somehow show which net we are generating for
public class GenerateFragment extends Fragment { public class GenerateFragment extends Fragment {
static final String TAG = "GenerateFragment";
EditText etWalletName; EditText etWalletName;
EditText etWalletPassword; EditText etWalletPassword;
EditText etWalletAddress;
EditText etWalletMnemonic;
LinearLayout llRestoreKeys;
EditText etWalletViewKey;
EditText etWalletSpendKey;
EditText etWalletRestoreHeight;
Button bGenerate; Button bGenerate;
LinearLayout llAccept;
TextView tvWalletAddress;
TextView tvWalletMnemonic;
Button bAccept;
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
@ -42,22 +61,27 @@ public class GenerateFragment extends Fragment {
etWalletName = (EditText) view.findViewById(R.id.etWalletName); etWalletName = (EditText) view.findViewById(R.id.etWalletName);
etWalletPassword = (EditText) view.findViewById(R.id.etWalletPassword); etWalletPassword = (EditText) view.findViewById(R.id.etWalletPassword);
etWalletMnemonic = (EditText) view.findViewById(R.id.etWalletMnemonic);
etWalletAddress = (EditText) view.findViewById(R.id.etWalletAddress);
llRestoreKeys = (LinearLayout) view.findViewById(R.id.llRestoreKeys);
etWalletViewKey = (EditText) view.findViewById(R.id.etWalletViewKey);
etWalletSpendKey = (EditText) view.findViewById(R.id.etWalletSpendKey);
etWalletRestoreHeight = (EditText) view.findViewById(R.id.etWalletRestoreHeight);
bGenerate = (Button) view.findViewById(R.id.bGenerate); bGenerate = (Button) view.findViewById(R.id.bGenerate);
llAccept = (LinearLayout) view.findViewById(R.id.llAccept);
tvWalletAddress = (TextView) view.findViewById(R.id.tvWalletAddress);
tvWalletMnemonic = (TextView) view.findViewById(R.id.tvWalletMnemonic);
bAccept = (Button) view.findViewById(R.id.bAccept);
boolean testnet = WalletManager.getInstance().isTestNet(); boolean testnet = WalletManager.getInstance().isTestNet();
tvWalletMnemonic.setTextIsSelectable(testnet); etWalletMnemonic.setTextIsSelectable(testnet);
etWalletName.requestFocus(); etWalletName.requestFocus();
Helper.showKeyboard(getActivity()); Helper.showKeyboard(getActivity());
setGenerateEnabled();
etWalletName.addTextChangedListener(new TextWatcher() { etWalletName.addTextChangedListener(new TextWatcher() {
@Override @Override
public void afterTextChanged(Editable editable) { public void afterTextChanged(Editable editable) {
showMnemonic(""); if (etWalletName.length() > 0) {
bGenerate.setEnabled(true);
} else {
bGenerate.setEnabled(false);
}
} }
@Override @Override
@ -76,11 +100,11 @@ public class GenerateFragment extends Fragment {
}); });
etWalletName.setOnEditorActionListener(new TextView.OnEditorActionListener() { etWalletName.setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) { if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) {
if (etWalletName.length() > 0) { if (etWalletName.length() > 0) {
etWalletPassword.requestFocus(); etWalletPassword.requestFocus();
} // otherwise ignore } // otherwise ignore
return false; return true;
} }
return false; return false;
} }
@ -92,10 +116,52 @@ public class GenerateFragment extends Fragment {
Helper.showKeyboard(getActivity()); Helper.showKeyboard(getActivity());
} }
}); });
etWalletPassword.addTextChangedListener(new TextWatcher() { etWalletPassword.setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) {
etWalletMnemonic.requestFocus();
return true;
}
return false;
}
});
etWalletMnemonic.setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) {
if (etWalletMnemonic.length() == 0) {
etWalletAddress.requestFocus();
} else if (mnemonicOk()) {
etWalletRestoreHeight.requestFocus();
} else {
Toast.makeText(getActivity(), getString(R.string.generate_check_mnemonic), Toast.LENGTH_LONG).show();
}
return true;
}
return false;
}
});
etWalletMnemonic.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.showKeyboard(getActivity());
}
});
etWalletMnemonic.addTextChangedListener(new TextWatcher() {
@Override @Override
public void afterTextChanged(Editable editable) { public void afterTextChanged(Editable editable) {
showMnemonic(""); if (etWalletMnemonic.length() > 0) {
etWalletRestoreHeight.setVisibility(View.VISIBLE);
etWalletAddress.setVisibility(View.INVISIBLE);
} else {
etWalletAddress.setVisibility(View.VISIBLE);
if (etWalletAddress.length() == 0) {
etWalletRestoreHeight.setVisibility(View.INVISIBLE);
} else {
etWalletRestoreHeight.setVisibility(View.VISIBLE);
}
}
} }
@Override @Override
@ -106,13 +172,104 @@ public class GenerateFragment extends Fragment {
public void onTextChanged(CharSequence s, int start, int before, int count) { public void onTextChanged(CharSequence s, int start, int before, int count) {
} }
}); });
etWalletPassword.setOnEditorActionListener(new TextView.OnEditorActionListener() {
etWalletAddress.setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) { if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) {
if (etWalletAddress.length() == 0) {
Helper.hideKeyboard(getActivity()); Helper.hideKeyboard(getActivity());
generateWallet(); generateWallet();
} else if (addressOk()) {
etWalletViewKey.requestFocus();
} else {
Toast.makeText(getActivity(), getString(R.string.generate_check_address), Toast.LENGTH_LONG).show();
}
return true;
}
return false; return false;
} }
});
etWalletAddress.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.showKeyboard(getActivity());
}
});
etWalletAddress.addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable editable) {
if (etWalletAddress.length() > 0) {
llRestoreKeys.setVisibility(View.VISIBLE);
etWalletMnemonic.setVisibility(View.INVISIBLE);
etWalletRestoreHeight.setVisibility(View.VISIBLE);
} else {
llRestoreKeys.setVisibility(View.INVISIBLE);
etWalletMnemonic.setVisibility(View.VISIBLE);
if (etWalletMnemonic.length() == 0) {
etWalletRestoreHeight.setVisibility(View.INVISIBLE);
} else {
etWalletRestoreHeight.setVisibility(View.VISIBLE);
}
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
});
etWalletViewKey.setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) {
if (viewKeyOk()) {
etWalletSpendKey.requestFocus();
} else {
Toast.makeText(getActivity(), getString(R.string.generate_check_key), Toast.LENGTH_LONG).show();
}
return true;
}
return false;
}
});
etWalletViewKey.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.showKeyboard(getActivity());
}
});
etWalletSpendKey.setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) {
if (spendKeyOk()) {
Helper.hideKeyboard(getActivity());
generateWallet();
} else {
Toast.makeText(getActivity(), getString(R.string.generate_check_key), Toast.LENGTH_LONG).show();
}
return true;
}
return false;
}
});
etWalletSpendKey.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.showKeyboard(getActivity());
}
});
etWalletRestoreHeight.setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) {
Helper.hideKeyboard(getActivity());
generateWallet();
return true;
}
return false; return false;
} }
}); });
@ -125,19 +282,29 @@ public class GenerateFragment extends Fragment {
} }
}); });
bAccept.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
acceptWallet();
}
});
bAccept.setEnabled(false);
llAccept.setVisibility(View.GONE);
return view; return view;
} }
private boolean mnemonicOk() {
String seed = etWalletMnemonic.getText().toString();
return (seed.split("\\s").length == 25); // 25 words
}
private boolean addressOk() {
String address = etWalletAddress.getText().toString();
return ((address.length() == 95) && ("49A".indexOf(address.charAt(0)) >= 0));
}
private boolean viewKeyOk() {
String viewKey = etWalletViewKey.getText().toString();
return (viewKey.length() == 64) && (viewKey.matches("^[0-9a-fA-F]+$"));
}
private boolean spendKeyOk() {
String spendKey = etWalletSpendKey.getText().toString();
return ((spendKey.length() == 0) || ((spendKey.length() == 64) && (spendKey.matches("^[0-9a-fA-F]+$"))));
}
private void generateWallet() { private void generateWallet() {
String name = etWalletName.getText().toString(); String name = etWalletName.getText().toString();
if (name.length() == 0) return; if (name.length() == 0) return;
@ -148,43 +315,56 @@ public class GenerateFragment extends Fragment {
return; return;
} }
String password = etWalletPassword.getText().toString(); String password = etWalletPassword.getText().toString();
bGenerate.setEnabled(false);
String seed = etWalletMnemonic.getText().toString();
String address = etWalletAddress.getText().toString();
long height;
try {
height = Long.parseLong(etWalletRestoreHeight.getText().toString());
} catch (NumberFormatException ex) {
Log.e(TAG, "Cannot parse " + etWalletRestoreHeight.getText().toString());
Log.e(TAG, ex.getLocalizedMessage());
height = 0; // Keep calm and carry on!
}
// figure out how we want to create this wallet
// A. from scratch
if ((seed.length() == 0) && (address.length() == 0)) {
bGenerate.setVisibility(View.INVISIBLE);
activityCallback.onGenerate(name, password); activityCallback.onGenerate(name, password);
} else
// B. from seed
if (mnemonicOk()) {
bGenerate.setVisibility(View.INVISIBLE);
activityCallback.onGenerate(name, password, seed, height);
} else
// C. from keys
if (addressOk() && viewKeyOk() && (spendKeyOk())) {
String viewKey = etWalletViewKey.getText().toString();
String spendKey = etWalletSpendKey.getText().toString();
bGenerate.setVisibility(View.INVISIBLE);
activityCallback.onGenerate(name, password, address, viewKey, spendKey, height);
} else
// D. none of the above :)
{
Toast.makeText(getActivity(), getString(R.string.generate_check_something), Toast.LENGTH_LONG).show();
}
} }
private void acceptWallet() { public void walletGenerateError() {
String name = etWalletName.getText().toString();
String password = etWalletPassword.getText().toString();
bAccept.setEnabled(false);
activityCallback.onAccept(name, password);
}
private void setGenerateEnabled() {
bGenerate.setEnabled(etWalletName.length() > 0); bGenerate.setEnabled(etWalletName.length() > 0);
} bGenerate.setVisibility(View.VISIBLE);
public void showMnemonic(String mnemonic) {
setGenerateEnabled();
if (mnemonic.length() > 0) {
tvWalletMnemonic.setText(mnemonic);
bAccept.setEnabled(true);
llAccept.setVisibility(View.VISIBLE);
} else {
if (llAccept.getVisibility() != View.GONE) {
tvWalletMnemonic.setText(getActivity().getString(R.string.generate_seed));
bAccept.setEnabled(false);
llAccept.setVisibility(View.GONE);
}
}
} }
GenerateFragment.Listener activityCallback; GenerateFragment.Listener activityCallback;
// Container Activity must implement this interface
public interface Listener { public interface Listener {
void onGenerate(String name, String password); void onGenerate(String name, String password);
void onAccept(String name, String password); void onGenerate(String name, String password, String seed, long height);
void onGenerate(String name, String password, String address, String viewKey, String spendKey, long height);
File getStorageRoot(); File getStorageRoot();
} }

View File

@ -0,0 +1,116 @@
/*
* Copyright (c) 2017 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet;
import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import com.m2049r.xmrwallet.model.WalletManager;
import java.text.NumberFormat;
// TODO: somehow show which net we are generating for
public class GenerateReviewFragment extends Fragment {
static final String TAG = "GenerateReviewFragment";
TextView tvWalletName;
TextView tvWalletPassword;
TextView tvWalletAddress;
TextView tvWalletMnemonic;
TextView tvWalletViewKey;
TextView tvWalletSpendKey;
Button bAccept;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.gen_review_fragment, container, false);
tvWalletName = (TextView) view.findViewById(R.id.tvWalletName);
tvWalletPassword = (TextView) view.findViewById(R.id.tvWalletPassword);
tvWalletAddress = (TextView) view.findViewById(R.id.tvWalletAddress);
tvWalletViewKey = (TextView) view.findViewById(R.id.tvWalletViewKey);
tvWalletSpendKey = (TextView) view.findViewById(R.id.tvWalletSpendKey);
tvWalletMnemonic = (TextView) view.findViewById(R.id.tvWalletMnemonic);
bAccept = (Button) view.findViewById(R.id.bAccept);
boolean testnet = WalletManager.getInstance().isTestNet();
tvWalletMnemonic.setTextIsSelectable(testnet);
bAccept.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
acceptWallet();
}
});
showDetails();
return view;
}
private void acceptWallet() {
String name = tvWalletName.getText().toString();
String password = tvWalletPassword.getText().toString();
bAccept.setEnabled(false);
activityCallback.onAccept(name, password);
}
public void showDetails() {
Bundle b = getArguments();
String name = b.getString("name");
String password = b.getString("password");
String address = b.getString("address");
String seed = b.getString("seed");
String view = b.getString("viewkey");
String spend = b.getString("spendkey");
long height = b.getLong("restoreHeight");
tvWalletName.setText(name);
tvWalletPassword.setText(password);
tvWalletAddress.setText(address);
tvWalletMnemonic.setText(seed);
tvWalletViewKey.setText(view);
tvWalletSpendKey.setText(spend);
NumberFormat formatter = NumberFormat.getInstance();
bAccept.setEnabled(true);
}
GenerateReviewFragment.Listener activityCallback;
public interface Listener {
void onAccept(String name, String password);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof GenerateReviewFragment.Listener) {
this.activityCallback = (GenerateReviewFragment.Listener) context;
} else {
throw new ClassCastException(context.toString()
+ " must implement Listener");
}
}
}

View File

@ -19,6 +19,7 @@ package com.m2049r.xmrwallet;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Fragment; import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction; import android.app.FragmentTransaction;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
@ -31,9 +32,7 @@ import android.util.Log;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
@ -46,7 +45,7 @@ import com.m2049r.xmrwallet.util.Helper;
import java.io.File; import java.io.File;
public class LoginActivity extends Activity public class LoginActivity extends Activity
implements LoginFragment.Listener, GenerateFragment.Listener { implements LoginFragment.Listener, GenerateFragment.Listener, GenerateReviewFragment.Listener {
static final String TAG = "LoginActivity"; static final String TAG = "LoginActivity";
static final int DAEMON_TIMEOUT = 500; // deamon must respond in 500ms static final int DAEMON_TIMEOUT = 500; // deamon must respond in 500ms
@ -219,14 +218,22 @@ public class LoginActivity extends Activity
} }
void startGenerateFragment() { void startGenerateFragment() {
replaceFragment(new GenerateFragment()); replaceFragment(new GenerateFragment(), "gen", null);
Log.d(TAG, "GenerateFragment placed"); Log.d(TAG, "GenerateFragment placed");
} }
void replaceFragment(Fragment newFragment) { void startReviewFragment(Bundle extras) {
replaceFragment(new GenerateReviewFragment(), null, extras);
Log.d(TAG, "GenerateReviewFragment placed");
}
void replaceFragment(Fragment newFragment, String name, Bundle extras) {
if (extras != null) {
newFragment.setArguments(extras);
}
FragmentTransaction transaction = getFragmentManager().beginTransaction(); FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, newFragment); transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null); transaction.addToBackStack(name);
transaction.commit(); transaction.commit();
} }
@ -235,33 +242,33 @@ public class LoginActivity extends Activity
////////////////////////////////////////// //////////////////////////////////////////
static final String MNEMONIC_LANGUAGE = "English"; // see mnemonics/electrum-words.cpp for more static final String MNEMONIC_LANGUAGE = "English"; // see mnemonics/electrum-words.cpp for more
@Override public void createWallet(final String name, final String password, final WalletCreator walletCreator) {
public void onGenerate(final String name, final String password) {
final GenerateFragment genFragment = (GenerateFragment) final GenerateFragment genFragment = (GenerateFragment)
getFragmentManager().findFragmentById(R.id.fragment_container); getFragmentManager().findFragmentById(R.id.fragment_container);
File newWalletFolder = new File(getStorageRoot(), ".new"); File newWalletFolder = new File(getStorageRoot(), ".new");
if (!newWalletFolder.exists()) { if (!newWalletFolder.exists()) {
if (!newWalletFolder.mkdir()) { if (!newWalletFolder.mkdir()) {
Log.e(TAG, "Cannot create new wallet dir " + newWalletFolder.getAbsolutePath()); Log.e(TAG, "Cannot create new wallet dir " + newWalletFolder.getAbsolutePath());
genFragment.showMnemonic(""); genFragment.walletGenerateError();
return; return;
} }
} }
if (!newWalletFolder.isDirectory()) { if (!newWalletFolder.isDirectory()) {
Log.e(TAG, "New wallet dir " + newWalletFolder.getAbsolutePath() + "is not a directory"); Log.e(TAG, "New wallet dir " + newWalletFolder.getAbsolutePath() + "is not a directory");
genFragment.showMnemonic(""); genFragment.walletGenerateError();
return; return;
} }
File cache = new File(newWalletFolder, name); File cacheFile = new File(newWalletFolder, name);
cache.delete(); cacheFile.delete();
File keys = new File(newWalletFolder, name + ".keys"); File keysFile = new File(newWalletFolder, name + ".keys");
keys.delete(); keysFile.delete();
File address = new File(newWalletFolder, name + ".address.txt"); final File addressFile = new File(newWalletFolder, name + ".address.txt");
address.delete(); addressFile.delete();
if (cache.exists() || keys.exists() || address.exists()) { if (cacheFile.exists() || keysFile.exists() || addressFile.exists()) {
Log.e(TAG, "Cannot remove all old wallet files: " + cache.getAbsolutePath()); Log.e(TAG, "Cannot remove all old wallet files: " + cacheFile.getAbsolutePath());
genFragment.showMnemonic(""); genFragment.walletGenerateError();
;
return; return;
} }
@ -271,17 +278,25 @@ public class LoginActivity extends Activity
@Override @Override
public void run() { public void run() {
Log.d(TAG, "creating wallet " + newWalletPath); Log.d(TAG, "creating wallet " + newWalletPath);
Wallet newWallet = WalletManager.getInstance() Wallet newWallet = walletCreator.createWallet(newWalletPath, password);
.createWallet(newWalletPath, password, MNEMONIC_LANGUAGE); final String seed = newWallet.getSeed();
Log.d(TAG, "wallet created"); final String address = newWallet.getAddress();
Log.d(TAG, "Created " + newWallet.getAddress()); final String view = newWallet.getSecretViewKey();
Log.d(TAG, "Seed " + newWallet.getSeed() + "."); final long height = newWallet.getBlockChainHeight();
final String mnemonic = newWallet.getSeed(); final String spend = "not available - use seed for recovery"; //TODO
newWallet.close(); newWallet.close();
Log.d(TAG, "Created " + address);
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
public void run() { public void run() {
if (genFragment.isAdded()) Bundle b = new Bundle();
genFragment.showMnemonic(mnemonic); b.putString("name", name);
b.putString("password", password);
b.putString("seed", seed);
b.putString("address", address);
b.putString("viewkey", view);
b.putString("spendkey", spend);
b.putLong("restoreHeight", height);
startReviewFragment(b);
} }
}); });
} }
@ -289,10 +304,54 @@ public class LoginActivity extends Activity
, "CreateWallet", MoneroHandlerThread.THREAD_STACK_SIZE).start(); , "CreateWallet", MoneroHandlerThread.THREAD_STACK_SIZE).start();
} }
interface WalletCreator {
Wallet createWallet(String path, String password);
}
@Override
public void onGenerate(String name, String password) {
createWallet(name, password,
new WalletCreator() {
public Wallet createWallet(String path, String password) {
return WalletManager.getInstance()
.createWallet(path, password, MNEMONIC_LANGUAGE);
}
});
}
@Override
public void onGenerate(String name, String password, final String seed, final long restoreHeight) {
createWallet(name, password,
new WalletCreator() {
public Wallet createWallet(String path, String password) {
Wallet newWallet = WalletManager.getInstance().recoveryWallet(path, seed, restoreHeight);
newWallet.setPassword(password);
newWallet.store();
return newWallet;
}
});
}
@Override
public void onGenerate(String name, String password,
final String address, final String viewKey, final String spendKey, final long restoreHeight) {
createWallet(name, password,
new WalletCreator() {
public Wallet createWallet(String path, String password) {
Wallet newWallet = WalletManager.getInstance()
.createWalletFromKeys(path, MNEMONIC_LANGUAGE, restoreHeight,
address, viewKey, spendKey);
newWallet.setPassword(password);
newWallet.store();
return newWallet;
}
});
}
@Override @Override
public void onAccept(final String name, final String password) { public void onAccept(final String name, final String password) {
final GenerateFragment genFragment = (GenerateFragment)
getFragmentManager().findFragmentById(R.id.fragment_container);
File newWalletFolder = new File(getStorageRoot(), ".new"); File newWalletFolder = new File(getStorageRoot(), ".new");
if (!newWalletFolder.isDirectory()) { if (!newWalletFolder.isDirectory()) {
Log.e(TAG, "New wallet dir " + newWalletFolder.getAbsolutePath() + "is not a directory"); Log.e(TAG, "New wallet dir " + newWalletFolder.getAbsolutePath() + "is not a directory");
@ -327,8 +386,8 @@ public class LoginActivity extends Activity
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
public void run() { public void run() {
if (rc) { if (rc) {
if (genFragment.isAdded()) getFragmentManager().popBackStack("gen",
getFragmentManager().popBackStack(); FragmentManager.POP_BACK_STACK_INCLUSIVE);
Toast.makeText(LoginActivity.this, Toast.makeText(LoginActivity.this,
getString(R.string.generate_wallet_created), Toast.LENGTH_SHORT).show(); getString(R.string.generate_wallet_created), Toast.LENGTH_SHORT).show();
} else { } else {

View File

@ -193,7 +193,7 @@ public class LoginFragment extends Fragment {
displayedList.clear(); displayedList.clear();
String x = isMainNet() ? "4" : "9A"; String x = isMainNet() ? "4" : "9A";
for (String s : walletList) { for (String s : walletList) {
Log.d(TAG, "filtering " + s); // Log.d(TAG, "filtering " + s);
if (x.indexOf(s.charAt(1)) >= 0) displayedList.add(s); if (x.indexOf(s.charAt(1)) >= 0) displayedList.add(s);
} }
displayedList.add(WALLETNAME_PREAMBLE + getString(R.string.generate_title)); displayedList.add(WALLETNAME_PREAMBLE + getString(R.string.generate_title));
@ -206,7 +206,7 @@ public class LoginFragment extends Fragment {
walletList.clear(); walletList.clear();
for (WalletManager.WalletInfo walletInfo : walletInfos) { for (WalletManager.WalletInfo walletInfo : walletInfos) {
Log.d(TAG, walletInfo.address); // Log.d(TAG, walletInfo.address);
String displayAddress = walletInfo.address; String displayAddress = walletInfo.address;
if (displayAddress.length() == 95) { if (displayAddress.length() == 95) {
displayAddress = walletInfo.address.substring(0, 6); displayAddress = walletInfo.address.substring(0, 6);

View File

@ -37,11 +37,13 @@ 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 java.text.NumberFormat;
import java.util.List; import java.util.List;
public class WalletFragment extends Fragment implements TransactionInfoAdapter.OnInteractionListener { public class WalletFragment extends Fragment implements TransactionInfoAdapter.OnInteractionListener {
private static final String TAG = "WalletFragment"; private static final String TAG = "WalletFragment";
private TransactionInfoAdapter adapter; private TransactionInfoAdapter adapter;
private NumberFormat formatter = NumberFormat.getInstance();
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
@ -171,7 +173,7 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
long daemonHeight = activityCallback.getDaemonHeight(); long daemonHeight = activityCallback.getDaemonHeight();
if (!wallet.isSynchronized()) { if (!wallet.isSynchronized()) {
long n = daemonHeight - wallet.getBlockChainHeight(); long n = daemonHeight - wallet.getBlockChainHeight();
sync = n + " " + getString(R.string.status_remaining); sync = formatter.format(n) + " " + getString(R.string.status_remaining);
if (firstBlock == 0) { if (firstBlock == 0) {
firstBlock = wallet.getBlockChainHeight(); firstBlock = wallet.getBlockChainHeight();
} }
@ -180,7 +182,7 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
if (x == 0) x = -1; if (x == 0) x = -1;
onProgress(x); onProgress(x);
} else { } else {
sync = getString(R.string.status_synced) + ": " + wallet.getBlockChainHeight(); sync = getString(R.string.status_synced) + ": " + formatter.format(wallet.getBlockChainHeight());
} }
} }
String net = (wallet.isTestNet() ? getString(R.string.connect_testnet) : getString(R.string.connect_mainnet)); String net = (wallet.isTestNet() ? getString(R.string.connect_testnet) : getString(R.string.connect_mainnet));

View File

@ -78,6 +78,8 @@ public class WalletManager {
return wallet; return wallet;
} }
private native long createWalletJ(String path, String password, String language, boolean isTestNet);
public Wallet openWallet(String path, String password) { public Wallet openWallet(String path, String password) {
long walletHandle = openWalletJ(path, password, isTestNet()); long walletHandle = openWalletJ(path, password, isTestNet());
Wallet wallet = new Wallet(walletHandle); Wallet wallet = new Wallet(walletHandle);
@ -85,6 +87,8 @@ public class WalletManager {
return wallet; return wallet;
} }
private native long openWalletJ(String path, String password, boolean isTestNet);
public Wallet recoveryWallet(String path, String mnemonic) { public Wallet recoveryWallet(String path, String mnemonic) {
Wallet wallet = recoveryWallet(path, mnemonic, 0); Wallet wallet = recoveryWallet(path, mnemonic, 0);
manageWallet(wallet.getName(), wallet); manageWallet(wallet.getName(), wallet);
@ -98,12 +102,17 @@ public class WalletManager {
return wallet; return wallet;
} }
private native long createWalletJ(String path, String password, String language, boolean isTestNet);
private native long openWalletJ(String path, String password, boolean isTestNet);
private native long recoveryWalletJ(String path, String mnemonic, boolean isTestNet, long restoreHeight); private native long recoveryWalletJ(String path, String mnemonic, boolean isTestNet, long restoreHeight);
public Wallet createWalletFromKeys(String path, String language, long restoreHeight,
String addressString, String viewKeyString, String spendKeyString) {
long walletHandle = createWalletFromKeysJ(path, language, isTestNet(), restoreHeight,
addressString, viewKeyString, spendKeyString);
Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet.getName(), wallet);
return wallet;
}
private native long createWalletFromKeysJ(String path, String language, private native long createWalletFromKeysJ(String path, String language,
boolean isTestNet, boolean isTestNet,
long restoreHeight, long restoreHeight,

View File

@ -419,6 +419,7 @@ public class WalletService extends Service {
WalletManager.getInstance().close(wallet); // TODO close() failed? WalletManager.getInstance().close(wallet); // TODO close() failed?
wallet = null; wallet = null;
// TODO what do we do with the progress?? // TODO what do we do with the progress??
// TODO tell the activity this failed
} }
} }
return wallet; return wallet;

View File

@ -2,108 +2,121 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<TextView <LinearLayout
android:layout_width="match_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="16dp" android:orientation="horizontal"
android:gravity="center" android:weightSum="2">
android:text="@string/generate_title"
android:textSize="30sp" />
<EditText <EditText
android:id="@+id/etWalletName" android:id="@+id/etWalletName"
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="16dp" android:layout_weight="1"
android:layout_marginEnd="8dp" android:hint="@string/generate_name_hint"
android:layout_marginStart="8dp" android:imeOptions="actionNext"
android:layout_marginTop="8dp"
android:backgroundTint="@color/colorPrimary"
android:focusable="true"
android:gravity="center"
android:hint="@string/generate_name"
android:imeOptions="actionDone"
android:inputType="text" android:inputType="text"
android:maxLines="1" android:maxLines="1"
android:textIsSelectable="true" android:textAlignment="center"
android:textSize="24sp" /> android:textSize="16sp" />
<EditText <EditText
android:id="@+id/etWalletPassword" android:id="@+id/etWalletPassword"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="@string/generate_password_hint"
android:imeOptions="actionNext"
android:inputType="text"
android:textAlignment="center"
android:textSize="16sp" />
</LinearLayout>
<EditText
android:id="@+id/etWalletMnemonic"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="16dp" android:hint="@string/generate_mnemonic_hint"
android:layout_marginEnd="8dp" android:imeOptions="actionNext"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:backgroundTint="@color/colorPrimary"
android:focusable="true"
android:gravity="center"
android:hint="@string/generate_password"
android:imeOptions="actionDone"
android:inputType="text" android:inputType="text"
android:maxLines="1" android:scrollHorizontally="false"
android:textIsSelectable="true" android:textAlignment="center"
android:textSize="24sp" /> android:textSize="16sp" />
<EditText
android:id="@+id/etWalletAddress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/generate_address_hint"
android:imeOptions="actionNext"
android:inputType="text"
android:textAlignment="center"
android:textSize="16sp" />
<LinearLayout
android:id="@+id/llRestoreKeys"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible">
<EditText
android:id="@+id/etWalletViewKey"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:hint="@string/generate_viewkey_hint"
android:imeOptions="actionNext"
android:inputType="text"
android:textAlignment="center"
android:textSize="16sp" />
<EditText
android:id="@+id/etWalletSpendKey"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/generate_spendkey_hint"
android:imeOptions="actionNext"
android:inputType="text"
android:textAlignment="center"
android:textSize="16sp" />
</LinearLayout>
<EditText
android:id="@+id/etWalletRestoreHeight"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/generate_restoreheight_hint"
android:imeOptions="actionNext"
android:inputType="number"
android:textAlignment="center"
android:textSize="16sp"
android:visibility="invisible" />
<Button <Button
android:id="@+id/bGenerate" android:id="@+id/bGenerate"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:background="@color/colorPrimary" android:background="@color/colorPrimary"
android:enabled="false"
android:text="@string/generate_buttonGenerate" /> android:text="@string/generate_buttonGenerate" />
<LinearLayout <!-- <Button
android:id="@+id/llAccept" android:id="@+id/bReset"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:layout_marginTop="16dp"
android:orientation="vertical"
android:visibility="visible">
<TextView
android:id="@+id/tvWalletAddress"
android:textIsSelectable="true"
android:selectAllOnFocus="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="@string/generate_address_placeholder"
android:textAlignment="center"
android:textColor="@color/menu_background_color"
android:textSize="12sp" />
<TextView
android:id="@+id/tvWalletMnemonic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="@string/generate_mnemonic_placeholder"
android:textAlignment="center"
android:textColor="@color/menu_background_color"
android:textSize="20sp" />
<Button
android:id="@+id/bAccept"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp"
android:background="@color/colorPrimary" android:background="@color/colorPrimary"
android:text="@string/generate_buttonAccept" /> android:text="@string/generate_button_reset" />
</LinearLayout> -->
</LinearLayout> </LinearLayout>

View File

@ -0,0 +1,162 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="2">
<TextView
android:id="@+id/tvWalletLabel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/generate_wallet_label"
android:textAlignment="center"
android:textColor="@color/colorAccent"
android:textSize="16sp" />
<TextView
android:id="@+id/tvWalletPasswordLabel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/generate_password_label"
android:textAlignment="center"
android:textColor="@color/colorAccent"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="2">
<TextView
android:id="@+id/tvWalletName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_weight="1"
android:text="@string/generate_name_hint"
android:textAlignment="center"
android:textColor="@color/colorPrimaryDark"
android:textSize="16sp" />
<TextView
android:id="@+id/tvWalletPassword"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_weight="1"
android:text="@string/generate_password_hint"
android:textAlignment="center"
android:textColor="@color/colorPrimaryDark"
android:textSize="16sp" />
</LinearLayout>
<TextView
android:id="@+id/tvWalletMnemonicLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/generate_mnemonic_label"
android:textAlignment="center"
android:textColor="@color/colorAccent"
android:textSize="16sp" />
<TextView
android:id="@+id/tvWalletMnemonic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="@string/generate_mnemonic_placeholder"
android:textAlignment="center"
android:textColor="@color/colorPrimaryDark"
android:textSize="16sp" />
<TextView
android:id="@+id/tvWalletAddressLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/generate_address_label"
android:textAlignment="center"
android:textColor="@color/colorAccent"
android:textSize="16sp" />
<TextView
android:id="@+id/tvWalletAddress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:selectAllOnFocus="true"
android:text="@string/generate_address_placeholder"
android:textAlignment="center"
android:textColor="@color/colorPrimaryDark"
android:textIsSelectable="true"
android:textSize="16sp" />
<TextView
android:id="@+id/tvWalletViewKeyLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/generate_viewkey_label"
android:textAlignment="center"
android:textColor="@color/colorAccent"
android:textSize="16sp" />
<TextView
android:id="@+id/tvWalletViewKey"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:selectAllOnFocus="true"
android:text="@string/generate_viewkey_placeholder"
android:textAlignment="center"
android:textColor="@color/colorPrimaryDark"
android:textIsSelectable="true"
android:textSize="16sp" />
<TextView
android:id="@+id/tvWalletSpendKeyLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/generate_spendkey_label"
android:textAlignment="center"
android:textColor="@color/colorAccent"
android:textSize="16sp" />
<TextView
android:id="@+id/tvWalletSpendKey"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:selectAllOnFocus="true"
android:text="@string/generate_spendkey_placeholder"
android:textAlignment="center"
android:textColor="@color/colorPrimaryDark"
android:textIsSelectable="true"
android:textSize="16sp" />
<Button
android:id="@+id/bAccept"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:background="@color/colorPrimary"
android:text="@string/generate_button_accept" />
</LinearLayout>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:gravity="center_horizontal" android:gravity="center_horizontal"
@ -7,12 +7,10 @@
android:paddingBottom="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" android:paddingTop="@dimen/activity_vertical_margin">
tools:context="com.m2049r.xmrwallet.LoginActivity">
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">

View File

@ -46,16 +46,41 @@
<string name="message_strorage_not_permitted">We really need those External Storage permissions!</string> <string name="message_strorage_not_permitted">We really need those External Storage permissions!</string>
<string name="generate_title">Generate Wallet</string> <string name="generate_title">Generate Wallet</string>
<string name="generate_name">Wallet Name</string> <string name="generate_name_hint">Wallet Name</string>
<string name="generate_password">Wallet Password</string> <string name="generate_password_hint">Wallet Password</string>
<string name="generate_buttonGenerate">Do it already!</string> <string name="generate_buttonGenerate">Do it already!</string>
<string name="generate_seed">Mnemonic Seed</string> <string name="generate_seed">Mnemonic Seed</string>
<string name="generate_buttonAccept">I have noted this mnemonic seed\nNow, I want to loose all my money!</string> <string name="generate_button_accept">I have noted the mnemonic seed\nNow, I want to loose all my money!</string>
<string name="generate_button_reset">I\'m confused - Let me start again!</string>
<string name="generate_wallet_exists">Wallet exists! Choose another name</string> <string name="generate_wallet_exists">Wallet exists! Choose another name</string>
<string name="generate_wallet_created">Wallet created</string> <string name="generate_wallet_created">Wallet created</string>
<string name="generate_wallet_create_failed_1">Wallet create failed (1/2)</string> <string name="generate_wallet_create_failed_1">Wallet create failed (1/2)</string>
<string name="generate_wallet_create_failed_2">Wallet create failed (2/2)</string> <string name="generate_wallet_create_failed_2">Wallet create failed (2/2)</string>
<string name="generate_address_placeholder">9tDC52GsMjTNt4dpnRCwAF7ekVBkbkgkXGaMKTcSTpBhGpqkPX56jCNRydLq9oGjbbAQBsZhLfgmTKsntmxRd3TaJFYM2f8</string> <string name="generate_address_placeholder">9tDC52GsMjTNt4dpnRCwAF7ekVBkbkgkXGaMKTcSTpBhGpqkPX56jCNRydLq9oGjbbAQBsZhLfgmTKsntmxRd3TaJFYM2f8</string>
<string name="generate_viewkey_placeholder">e2b99f4cc3d644774c4b118db05f8aa9967583a01ca4d47058c3860af10bd306</string>
<string name="generate_spendkey_placeholder">300a54208ab0a638a8407a12e3de946da76f5a9ded303338452332ec7755210d</string>
<string name="generate_mnemonic_placeholder">camp feline inflamed memoir afloat eight alerts females gutter cogs menu waveform gather tawny judge gusts yahoo doctor females biscuit alchemy reef agony austere camp</string> <string name="generate_mnemonic_placeholder">camp feline inflamed memoir afloat eight alerts females gutter cogs menu waveform gather tawny judge gusts yahoo doctor females biscuit alchemy reef agony austere camp</string>
<string name="generate_restoreheight_placeholder">1307882</string>
<string name="generate_address_hint">Public Address (optional)</string>
<string name="generate_viewkey_hint">View Key</string>
<string name="generate_spendkey_hint">Spend Key (optional)</string>
<string name="generate_mnemonic_hint">Mnemonic Seed (optional)</string>
<string name="generate_restoreheight_hint">Restore Height (optional)</string>
<string name="generate_wallet_label">Wallet</string>
<string name="generate_password_label">Password</string>
<string name="generate_address_label">Public Address</string>
<string name="generate_viewkey_label">View Key</string>
<string name="generate_spendkey_label">Spend Key</string>
<string name="generate_mnemonic_label">Mnemonic Seed</string>
<string name="generate_restoreheight_label">Restore Height:</string>
<string name="generate_check_keys">Check your keys!</string>
<string name="generate_check_key">Check your key!</string>
<string name="generate_check_address">Check your address!</string>
<string name="generate_check_mnemonic">Check your mnemonic seed!</string>
<string name="generate_check_something">Check your entry!</string>
</resources> </resources>