diff --git a/app/src/main/cpp/monerujo.cpp b/app/src/main/cpp/monerujo.cpp index 79be0389..1d14de8a 100644 --- a/app/src/main/cpp/monerujo.cpp +++ b/app/src/main/cpp/monerujo.cpp @@ -302,7 +302,7 @@ Java_com_m2049r_xmrwallet_model_WalletManager_recoveryWalletJ(JNIEnv *env, jobje return reinterpret_cast(wallet); } -JNIEXPORT jboolean JNICALL +JNIEXPORT jlong JNICALL Java_com_m2049r_xmrwallet_model_WalletManager_createWalletFromKeysJ(JNIEnv *env, jobject instance, jstring path, jstring language, jboolean isTestNet, diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java index 5ef7b2b3..d1f846ad 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java @@ -21,7 +21,6 @@ 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; @@ -38,8 +37,6 @@ import com.m2049r.xmrwallet.util.Helper; import java.io.File; -// TODO: somehow show which net we are generating for - public class GenerateFragment extends Fragment { static final String TAG = "GenerateFragment"; @@ -119,7 +116,11 @@ public class GenerateFragment extends Fragment { 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(); + if (etWalletAddress.length() > 0) { + etWalletAddress.requestFocus(); + } else { + etWalletMnemonic.requestFocus(); + } return true; } return false; @@ -141,39 +142,45 @@ public class GenerateFragment extends Fragment { return false; } }); - etWalletMnemonic.setOnClickListener(new View.OnClickListener() { + etWalletMnemonic.setOnClickListener(new View.OnClickListener() + + { @Override public void onClick(View v) { Helper.showKeyboard(getActivity()); } }); - etWalletMnemonic.addTextChangedListener(new TextWatcher() { - @Override - public void afterTextChanged(Editable editable) { - 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); - } + etWalletMnemonic.addTextChangedListener(new - } - } + TextWatcher() { + @Override + public void afterTextChanged(Editable editable) { + 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 - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } + } + } - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - }); + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } - etWalletAddress.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + }); + + 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_NEXT)) { if (etWalletAddress.length() == 0) { @@ -191,40 +198,46 @@ public class GenerateFragment extends Fragment { return false; } }); - etWalletAddress.setOnClickListener(new View.OnClickListener() { + 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); - } - } - } + etWalletAddress.addTextChangedListener(new - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } + 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 onTextChanged(CharSequence s, int start, int before, int count) { - } - }); + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } - etWalletViewKey.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @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()) { @@ -237,21 +250,22 @@ public class GenerateFragment extends Fragment { return false; } }); - etWalletViewKey.setOnClickListener(new View.OnClickListener() { + etWalletViewKey.setOnClickListener(new View.OnClickListener() + + { @Override public void onClick(View v) { Helper.showKeyboard(getActivity()); } }); - etWalletSpendKey.setOnEditorActionListener(new TextView.OnEditorActionListener() { + 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()) { - if (bGenerate.getVisibility() == View.VISIBLE) { - Helper.hideKeyboard(getActivity()); - generateWallet(); - } + etWalletRestoreHeight.requestFocus(); } else { Toast.makeText(getActivity(), getString(R.string.generate_check_key), Toast.LENGTH_LONG).show(); } @@ -260,14 +274,18 @@ public class GenerateFragment extends Fragment { return false; } }); - etWalletSpendKey.setOnClickListener(new View.OnClickListener() { + etWalletSpendKey.setOnClickListener(new View.OnClickListener() + + { @Override public void onClick(View v) { Helper.showKeyboard(getActivity()); } }); - etWalletRestoreHeight.setOnEditorActionListener(new TextView.OnEditorActionListener() { + 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)) { if (bGenerate.getVisibility() == View.VISIBLE) { @@ -280,7 +298,9 @@ public class GenerateFragment extends Fragment { } }); - bGenerate.setOnClickListener(new View.OnClickListener() { + bGenerate.setOnClickListener(new View.OnClickListener() + + { @Override public void onClick(View v) { Helper.hideKeyboard(getActivity()); diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java index 71a145b7..8089323d 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java @@ -27,8 +27,6 @@ import android.widget.TextView; import com.m2049r.xmrwallet.model.WalletManager; -// TODO: somehow show which net we are generating for - public class GenerateReviewFragment extends Fragment { static final String TAG = "GenerateReviewFragment"; @@ -84,13 +82,17 @@ public class GenerateReviewFragment extends Fragment { 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); + if (spend.length() > 0) { // should be == 64, but spendkey is not in the API yet + tvWalletSpendKey.setText(spend); + } else { + tvWalletSpendKey.setText(getString(R.string.generate_wallet_watchonly)); + } bAccept.setEnabled(true); } diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java index b9cba287..1ddc8df8 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java @@ -27,6 +27,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Bundle; +import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.v7.app.AppCompatActivity; import android.util.Log; @@ -44,6 +45,10 @@ import com.m2049r.xmrwallet.service.MoneroHandlerThread; import com.m2049r.xmrwallet.util.Helper; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; public class LoginActivity extends AppCompatActivity implements LoginFragment.Listener, GenerateFragment.Listener, GenerateReviewFragment.Listener { @@ -288,8 +293,7 @@ public class LoginActivity extends AppCompatActivity 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 + final String spend = newWallet.isWatchOnly() ? "" : "not available - use seed for recovery"; newWallet.close(); Log.d(TAG, "Created " + address); runOnUiThread(new Runnable() { @@ -301,7 +305,6 @@ public class LoginActivity extends AppCompatActivity b.putString("address", address); b.putString("viewkey", view); b.putString("spendkey", spend); - b.putLong("restoreHeight", height); startReviewFragment(b); } }); @@ -321,7 +324,6 @@ public class LoginActivity extends AppCompatActivity public Wallet createWallet(String path, String password) { return WalletManager.getInstance() .createWallet(path, password, MNEMONIC_LANGUAGE); - } }); } @@ -358,37 +360,16 @@ public class LoginActivity extends AppCompatActivity @Override public void onAccept(final String name, final String password) { - File newWalletFolder = new File(getStorageRoot(), ".new"); - if (!newWalletFolder.isDirectory()) { - Log.e(TAG, "New wallet dir " + newWalletFolder.getAbsolutePath() + "is not a directory"); - return; - } - final String newWalletPath = new File(newWalletFolder, name).getAbsolutePath(); + final File newWalletFolder = new File(getStorageRoot(), ".new"); + final File walletFolder = getStorageRoot(); new Thread(null, new Runnable() { @Override public void run() { - Log.d(TAG, "opening wallet " + newWalletPath); - Wallet newWallet = WalletManager.getInstance() - .openWallet(newWalletPath, password); - Wallet.Status status = newWallet.getStatus(); - Log.d(TAG, "wallet opened " + newWallet.getStatus()); - if (status != Wallet.Status.Status_Ok) { - Log.e(TAG, "New wallet is " + status.toString()); - runOnUiThread(new Runnable() { - public void run() { - Toast.makeText(LoginActivity.this, - getString(R.string.generate_wallet_create_failed_1), Toast.LENGTH_LONG).show(); - } - }); - newWallet.close(); - return; - } - final String walletPath = new File(getStorageRoot(), name).getAbsolutePath(); - final boolean rc = newWallet.store(walletPath); - Log.d(TAG, "wallet stored with rc=" + rc); - newWallet.close(); - Log.d(TAG, "wallet closed"); + final String walletPath = new File(walletFolder, name).getAbsolutePath(); + final boolean rc = copyWallet(walletFolder, newWalletFolder, name) + && + (testWallet(walletPath, password) == Wallet.Status.Status_Ok); runOnUiThread(new Runnable() { public void run() { if (rc) { @@ -408,4 +389,45 @@ public class LoginActivity extends AppCompatActivity , "AcceptWallet", MoneroHandlerThread.THREAD_STACK_SIZE).start(); } + Wallet.Status testWallet(String path, String password) { + Log.d(TAG, "testing wallet " + path); + Wallet aWallet = WalletManager.getInstance().openWallet(path, password); + if (aWallet == null) return Wallet.Status.Status_Error; // does this ever happen? + Wallet.Status status = aWallet.getStatus(); + Log.d(TAG, "wallet tested " + aWallet.getStatus()); + aWallet.close(); + return status; + } + + boolean copyWallet(File dstDir, File srcDir, String name) { + boolean success = false; + try { + // TODO: the cache is corrupt if we recover (!!) + // TODO recoveryheight is ignored but not on watchonly wallet ?! - find out why + //copyFile(dstDir, srcDir, name); + copyFile(dstDir, srcDir, name + ".keys"); + copyFile(dstDir, srcDir, name + ".address.txt"); + success = true; + } catch (IOException ex) { + Log.e(TAG, "wallet copy failed: " + ex.getMessage()); + // try to rollback + new File(dstDir, name).delete(); + new File(dstDir, name + ".keys").delete(); + new File(dstDir, name + ".address.txt").delete(); + } + return success; + } + + void copyFile(File dstDir, File srcDir, String name) throws IOException { + FileChannel inChannel = new FileInputStream(new File(srcDir, name)).getChannel(); + FileChannel outChannel = new FileOutputStream(new File(dstDir, name)).getChannel(); + try { + inChannel.transferTo(0, inChannel.size(), outChannel); + } finally { + if (inChannel != null) + inChannel.close(); + if (outChannel != null) + outChannel.close(); + } + } } 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 f67ab9b8..ce4bc3c9 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java +++ b/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java @@ -111,7 +111,8 @@ public class WalletService extends Service { fullRefresh = true; } } - observer.onRefreshed(wallet, fullRefresh); + if (observer != null) + observer.onRefreshed(wallet, fullRefresh); } } } @@ -354,6 +355,7 @@ public class WalletService extends Service { if (listener == null) { Log.d(TAG, "start() loadWallet"); Wallet aWallet = loadWallet(walletName, walletPassword); + // TODO check aWallet and die gracefully if neccessary listener = new MyWalletListener(aWallet); listener.start(); showProgress(100); @@ -420,6 +422,7 @@ public class WalletService extends Service { wallet = null; // TODO what do we do with the progress?? // TODO tell the activity this failed + // this crashes in MyWalletListener(Wallet aWallet) as wallet == null } } return wallet; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6f27b22c..9ec235a0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -53,6 +53,8 @@ I have noted the mnemonic seed\nNow, I want to loose all my money! I\'m confused - Let me start again! + <Watch Only Wallet> + Wallet exists! Choose another name Wallet created Wallet create failed (1/2)