diff --git a/.idea/misc.xml b/.idea/misc.xml index fbb6828..5d19981 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -37,7 +37,7 @@ - + diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java index 9fc842d..56dfae2 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java @@ -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; import android.app.Fragment; @@ -5,6 +21,7 @@ import android.content.Context; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; +import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; @@ -24,15 +41,17 @@ import java.io.File; // TODO: somehow show which net we are generating for public class GenerateFragment extends Fragment { + static final String TAG = "GenerateFragment"; EditText etWalletName; EditText etWalletPassword; + EditText etWalletAddress; + EditText etWalletMnemonic; + LinearLayout llRestoreKeys; + EditText etWalletViewKey; + EditText etWalletSpendKey; + EditText etWalletRestoreHeight; Button bGenerate; - LinearLayout llAccept; - TextView tvWalletAddress; - TextView tvWalletMnemonic; - Button bAccept; - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -42,22 +61,27 @@ public class GenerateFragment extends Fragment { etWalletName = (EditText) view.findViewById(R.id.etWalletName); 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); - 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(); - tvWalletMnemonic.setTextIsSelectable(testnet); + etWalletMnemonic.setTextIsSelectable(testnet); etWalletName.requestFocus(); Helper.showKeyboard(getActivity()); - setGenerateEnabled(); etWalletName.addTextChangedListener(new TextWatcher() { @Override public void afterTextChanged(Editable editable) { - showMnemonic(""); + if (etWalletName.length() > 0) { + bGenerate.setEnabled(true); + } else { + bGenerate.setEnabled(false); + } } @Override @@ -76,11 +100,11 @@ public class GenerateFragment extends Fragment { }); etWalletName.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_DONE)) { + if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) { if (etWalletName.length() > 0) { etWalletPassword.requestFocus(); } // otherwise ignore - return false; + return true; } return false; } @@ -92,10 +116,52 @@ public class GenerateFragment extends Fragment { 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 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 @@ -106,12 +172,103 @@ public class GenerateFragment extends Fragment { 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) { - 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()); + generateWallet(); + } else if (addressOk()) { + etWalletViewKey.requestFocus(); + } else { + Toast.makeText(getActivity(), getString(R.string.generate_check_address), Toast.LENGTH_LONG).show(); + } + return true; + } + 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 false; + return true; } 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; } + 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() { String name = etWalletName.getText().toString(); if (name.length() == 0) return; @@ -148,43 +315,56 @@ public class GenerateFragment extends Fragment { return; } String password = etWalletPassword.getText().toString(); - bGenerate.setEnabled(false); - activityCallback.onGenerate(name, password); - } - private void acceptWallet() { - String name = etWalletName.getText().toString(); - String password = etWalletPassword.getText().toString(); - bAccept.setEnabled(false); - activityCallback.onAccept(name, password); - } + String seed = etWalletMnemonic.getText().toString(); + String address = etWalletAddress.getText().toString(); - private void setGenerateEnabled() { - bGenerate.setEnabled(etWalletName.length() > 0); - } - - 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); - } + 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); + } 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(); + } + } + + public void walletGenerateError() { + bGenerate.setEnabled(etWalletName.length() > 0); + bGenerate.setVisibility(View.VISIBLE); } GenerateFragment.Listener activityCallback; - // Container Activity must implement this interface public interface Listener { 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(); } diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java new file mode 100644 index 0000000..38be477 --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java @@ -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"); + } + } +} diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java index b9becc4..0d3d76d 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java @@ -19,6 +19,7 @@ package com.m2049r.xmrwallet; import android.app.Activity; import android.app.AlertDialog; import android.app.Fragment; +import android.app.FragmentManager; import android.app.FragmentTransaction; import android.content.Context; import android.content.DialogInterface; @@ -31,9 +32,7 @@ import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; -import android.view.WindowManager; import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; @@ -46,7 +45,7 @@ import com.m2049r.xmrwallet.util.Helper; import java.io.File; public class LoginActivity extends Activity - implements LoginFragment.Listener, GenerateFragment.Listener { + implements LoginFragment.Listener, GenerateFragment.Listener, GenerateReviewFragment.Listener { static final String TAG = "LoginActivity"; static final int DAEMON_TIMEOUT = 500; // deamon must respond in 500ms @@ -219,14 +218,22 @@ public class LoginActivity extends Activity } void startGenerateFragment() { - replaceFragment(new GenerateFragment()); + replaceFragment(new GenerateFragment(), "gen", null); 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(); transaction.replace(R.id.fragment_container, newFragment); - transaction.addToBackStack(null); + transaction.addToBackStack(name); transaction.commit(); } @@ -235,33 +242,33 @@ public class LoginActivity extends Activity ////////////////////////////////////////// static final String MNEMONIC_LANGUAGE = "English"; // see mnemonics/electrum-words.cpp for more - @Override - public void onGenerate(final String name, final String password) { + public void createWallet(final String name, final String password, final WalletCreator walletCreator) { final GenerateFragment genFragment = (GenerateFragment) getFragmentManager().findFragmentById(R.id.fragment_container); File newWalletFolder = new File(getStorageRoot(), ".new"); if (!newWalletFolder.exists()) { if (!newWalletFolder.mkdir()) { Log.e(TAG, "Cannot create new wallet dir " + newWalletFolder.getAbsolutePath()); - genFragment.showMnemonic(""); + genFragment.walletGenerateError(); return; } } if (!newWalletFolder.isDirectory()) { Log.e(TAG, "New wallet dir " + newWalletFolder.getAbsolutePath() + "is not a directory"); - genFragment.showMnemonic(""); + genFragment.walletGenerateError(); return; } - File cache = new File(newWalletFolder, name); - cache.delete(); - File keys = new File(newWalletFolder, name + ".keys"); - keys.delete(); - File address = new File(newWalletFolder, name + ".address.txt"); - address.delete(); + File cacheFile = new File(newWalletFolder, name); + cacheFile.delete(); + File keysFile = new File(newWalletFolder, name + ".keys"); + keysFile.delete(); + final File addressFile = new File(newWalletFolder, name + ".address.txt"); + addressFile.delete(); - if (cache.exists() || keys.exists() || address.exists()) { - Log.e(TAG, "Cannot remove all old wallet files: " + cache.getAbsolutePath()); - genFragment.showMnemonic(""); + if (cacheFile.exists() || keysFile.exists() || addressFile.exists()) { + Log.e(TAG, "Cannot remove all old wallet files: " + cacheFile.getAbsolutePath()); + genFragment.walletGenerateError(); + ; return; } @@ -271,17 +278,25 @@ public class LoginActivity extends Activity @Override public void run() { Log.d(TAG, "creating wallet " + newWalletPath); - Wallet newWallet = WalletManager.getInstance() - .createWallet(newWalletPath, password, MNEMONIC_LANGUAGE); - Log.d(TAG, "wallet created"); - Log.d(TAG, "Created " + newWallet.getAddress()); - Log.d(TAG, "Seed " + newWallet.getSeed() + "."); - final String mnemonic = newWallet.getSeed(); + Wallet newWallet = walletCreator.createWallet(newWalletPath, password); + final String seed = newWallet.getSeed(); + final String address = newWallet.getAddress(); + final String view = newWallet.getSecretViewKey(); + final long height = newWallet.getBlockChainHeight(); + final String spend = "not available - use seed for recovery"; //TODO newWallet.close(); + Log.d(TAG, "Created " + address); runOnUiThread(new Runnable() { public void run() { - if (genFragment.isAdded()) - genFragment.showMnemonic(mnemonic); + Bundle b = new Bundle(); + 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(); } + 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 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"); if (!newWalletFolder.isDirectory()) { Log.e(TAG, "New wallet dir " + newWalletFolder.getAbsolutePath() + "is not a directory"); @@ -327,8 +386,8 @@ public class LoginActivity extends Activity runOnUiThread(new Runnable() { public void run() { if (rc) { - if (genFragment.isAdded()) - getFragmentManager().popBackStack(); + getFragmentManager().popBackStack("gen", + FragmentManager.POP_BACK_STACK_INCLUSIVE); Toast.makeText(LoginActivity.this, getString(R.string.generate_wallet_created), Toast.LENGTH_SHORT).show(); } else { diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java index 3ab58db..ae05a40 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java @@ -193,7 +193,7 @@ public class LoginFragment extends Fragment { displayedList.clear(); String x = isMainNet() ? "4" : "9A"; for (String s : walletList) { - Log.d(TAG, "filtering " + s); + // Log.d(TAG, "filtering " + s); if (x.indexOf(s.charAt(1)) >= 0) displayedList.add(s); } displayedList.add(WALLETNAME_PREAMBLE + getString(R.string.generate_title)); @@ -206,7 +206,7 @@ public class LoginFragment extends Fragment { walletList.clear(); for (WalletManager.WalletInfo walletInfo : walletInfos) { - Log.d(TAG, walletInfo.address); + // Log.d(TAG, walletInfo.address); String displayAddress = walletInfo.address; if (displayAddress.length() == 95) { displayAddress = walletInfo.address.substring(0, 6); diff --git a/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java b/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java index ee4a636..c708b3c 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java @@ -37,11 +37,13 @@ import com.m2049r.xmrwallet.layout.TransactionInfoAdapter; import com.m2049r.xmrwallet.model.TransactionInfo; import com.m2049r.xmrwallet.model.Wallet; +import java.text.NumberFormat; import java.util.List; public class WalletFragment extends Fragment implements TransactionInfoAdapter.OnInteractionListener { private static final String TAG = "WalletFragment"; private TransactionInfoAdapter adapter; + private NumberFormat formatter = NumberFormat.getInstance(); @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -171,7 +173,7 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O long daemonHeight = activityCallback.getDaemonHeight(); if (!wallet.isSynchronized()) { long n = daemonHeight - wallet.getBlockChainHeight(); - sync = n + " " + getString(R.string.status_remaining); + sync = formatter.format(n) + " " + getString(R.string.status_remaining); if (firstBlock == 0) { firstBlock = wallet.getBlockChainHeight(); } @@ -180,7 +182,7 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O if (x == 0) x = -1; onProgress(x); } 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)); diff --git a/app/src/main/java/com/m2049r/xmrwallet/model/WalletManager.java b/app/src/main/java/com/m2049r/xmrwallet/model/WalletManager.java index 54782cf..357027b 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/model/WalletManager.java +++ b/app/src/main/java/com/m2049r/xmrwallet/model/WalletManager.java @@ -78,6 +78,8 @@ public class WalletManager { return wallet; } + private native long createWalletJ(String path, String password, String language, boolean isTestNet); + public Wallet openWallet(String path, String password) { long walletHandle = openWalletJ(path, password, isTestNet()); Wallet wallet = new Wallet(walletHandle); @@ -85,6 +87,8 @@ public class WalletManager { return wallet; } + private native long openWalletJ(String path, String password, boolean isTestNet); + public Wallet recoveryWallet(String path, String mnemonic) { Wallet wallet = recoveryWallet(path, mnemonic, 0); manageWallet(wallet.getName(), wallet); @@ -98,12 +102,17 @@ public class WalletManager { 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); + 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, boolean isTestNet, long restoreHeight, diff --git a/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java b/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java index b40f3a8..f67ab9b 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java +++ b/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java @@ -419,6 +419,7 @@ public class WalletService extends Service { WalletManager.getInstance().close(wallet); // TODO close() failed? wallet = null; // TODO what do we do with the progress?? + // TODO tell the activity this failed } } return wallet; diff --git a/app/src/main/res/layout/gen_fragment.xml b/app/src/main/res/layout/gen_fragment.xml index e0ed6a9..2fcf115 100644 --- a/app/src/main/res/layout/gen_fragment.xml +++ b/app/src/main/res/layout/gen_fragment.xml @@ -2,108 +2,121 @@ + 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"> - + android:orientation="horizontal" + android:weightSum="2"> + + + + + + android:scrollHorizontally="false" + android:textAlignment="center" + android:textSize="16sp" /> + android:textAlignment="center" + android:textSize="16sp" /> + + + + + + + + +