diff --git a/.idea/.gitignore b/.idea/.gitignore index 6faf083..45c1d3b 100644 --- a/.idea/.gitignore +++ b/.idea/.gitignore @@ -1,2 +1,2 @@ workspace.xml -markdown-navigator* +markdown-* diff --git a/app/src/main/cpp/monerujo.cpp b/app/src/main/cpp/monerujo.cpp index 088867e..edcb635 100644 --- a/app/src/main/cpp/monerujo.cpp +++ b/app/src/main/cpp/monerujo.cpp @@ -461,7 +461,6 @@ Java_com_m2049r_xmrwallet_model_WalletManager_resolveOpenAlias(JNIEnv *env, jobj //TODO static std::tuple checkUpdates(const std::string &software, const std::string &subdir); -// actually a WalletManager function, but logically in onWalletSelected JNIEXPORT jboolean JNICALL Java_com_m2049r_xmrwallet_model_WalletManager_closeJ(JNIEnv *env, jobject instance, jobject walletInstance) { diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java index a5d87c3..c3a7f07 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java @@ -295,6 +295,10 @@ public class GenerateFragment extends Fragment { private void generateWallet() { String name = etWalletName.getText().toString(); if (name.length() == 0) return; + if (name.charAt(0)=='.') { + Toast.makeText(getActivity(), getString(R.string.generate_wallet_dot), Toast.LENGTH_LONG).show(); + etWalletName.requestFocus(); + } File walletFile = Helper.getWalletFile(getActivity(), name); if (WalletManager.getInstance().walletExists(walletFile)) { Toast.makeText(getActivity(), getString(R.string.generate_wallet_exists), Toast.LENGTH_LONG).show(); diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java index 5bfc9f6..9c02f9c 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java @@ -16,12 +16,15 @@ package com.m2049r.xmrwallet; +import android.app.Activity; import android.app.AlertDialog; +import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.Fragment; @@ -77,12 +80,18 @@ public class LoginActivity extends AppCompatActivity } } - @Override - public void onWalletSelected(final String walletName) { + boolean checkServiceRunning() { if (WalletService.Running) { Toast.makeText(this, getString(R.string.service_busy), Toast.LENGTH_SHORT).show(); - return; + return true; + } else { + return false; } + } + + @Override + public void onWalletSelected(final String walletName) { + if (checkServiceRunning()) return; Log.d(TAG, "selected wallet is ." + walletName + "."); // now it's getting real, check if wallet exists File walletFile = Helper.getWalletFile(this, walletName); @@ -101,7 +110,7 @@ public class LoginActivity extends AppCompatActivity @Override public void onWalletDetails(final String walletName) { Log.d(TAG, "details for wallet ." + walletName + "."); - + if (checkServiceRunning()) return; DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { @@ -138,6 +147,7 @@ public class LoginActivity extends AppCompatActivity @Override public void onWalletReceive(String walletName) { Log.d(TAG, "receive for wallet ." + walletName + "."); + if (checkServiceRunning()) return; final File walletFile = Helper.getWalletFile(this, walletName); if (WalletManager.getInstance().walletExists(walletFile)) { promptPassword(walletName, new PasswordAction() { @@ -151,8 +161,224 @@ public class LoginActivity extends AppCompatActivity } } + private class AsyncRename extends AsyncTask { + ProgressDialog progressDialog = new MyProgressDialog(LoginActivity.this, R.string.rename_progress); + + @Override + protected void onPreExecute() { + super.onPreExecute(); + progressDialog.show(); + LoginActivity.this.asyncWaitTask = this; + } + + @Override + protected Boolean doInBackground(String... params) { + if (params.length != 2) return false; + File walletFile = Helper.getWalletFile(LoginActivity.this, params[0]); + String newName = params[1]; + return renameWallet(walletFile, newName); + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + progressDialog.dismiss(); + if (result) { + reloadWalletList(); + } else { + Toast.makeText(LoginActivity.this, getString(R.string.rename_failed), Toast.LENGTH_LONG).show(); + } + LoginActivity.this.asyncWaitTask = null; + } + } + + // copy + delete seems safer than rename bevause we call rollback easily + boolean renameWallet(File walletFile, String newName) { + if (copyWallet(walletFile, new File(walletFile.getParentFile(), newName), false)) { + deleteWallet(walletFile); + return true; + } else { + return false; + } + } + + @Override + public void onWalletRename(final String walletName) { + Log.d(TAG, "rename for wallet ." + walletName + "."); + if (checkServiceRunning()) return; + LayoutInflater li = LayoutInflater.from(this); + View promptsView = li.inflate(R.layout.prompt_rename, null); + + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); + alertDialogBuilder.setView(promptsView); + + final EditText etRename = (EditText) promptsView.findViewById(R.id.etRename); + final TextView tvRenameLabel = (TextView) promptsView.findViewById(R.id.tvRenameLabel); + + tvRenameLabel.setText(getString(R.string.prompt_rename) + " " + walletName); + + // set dialog message + alertDialogBuilder + .setCancelable(false) + .setPositiveButton("OK", + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + Helper.hideKeyboardAlways(LoginActivity.this); + String newName = etRename.getText().toString(); + new AsyncRename().execute(walletName, newName); + } + }) + .setNegativeButton("Cancel", + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + Helper.hideKeyboardAlways(LoginActivity.this); + dialog.cancel(); + } + }); + + final AlertDialog dialog = alertDialogBuilder.create(); + Helper.showKeyboard(dialog); + + // accept keyboard "ok" + etRename.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)) { + Helper.hideKeyboardAlways(LoginActivity.this); + String newName = etRename.getText().toString(); + dialog.cancel(); + new AsyncRename().execute(walletName, newName); + return false; + } + return false; + } + }); + + dialog.show(); + } + + + private class AsyncBackup extends AsyncTask { + ProgressDialog progressDialog = new MyProgressDialog(LoginActivity.this, R.string.backup_progress); + + @Override + protected void onPreExecute() { + super.onPreExecute(); + progressDialog.show(); + LoginActivity.this.asyncWaitTask = this; + } + + @Override + protected Boolean doInBackground(String... params) { + if (params.length != 1) return false; + return backupWallet(params[0]); + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + progressDialog.dismiss(); + if (!result) { + Toast.makeText(LoginActivity.this, getString(R.string.backup_failed), Toast.LENGTH_LONG).show(); + } + LoginActivity.this.asyncWaitTask = null; + } + } + + private boolean backupWallet(String walletName) { + File backupFolder = new File(getStorageRoot(), ".backups"); + if (!backupFolder.exists()) { + if (!backupFolder.mkdir()) { + Log.e(TAG, "Cannot create backup dir " + backupFolder.getAbsolutePath()); + return false; + } + } + File walletFile = Helper.getWalletFile(LoginActivity.this, walletName); + File backupFile = new File(backupFolder, walletName); + Log.d(TAG, "backup " + walletFile.getAbsolutePath() + " to " + backupFile.getAbsolutePath()); + // TODO probably better to copy to a new file and then rename + // then if something fails we have the old backup at least + // or just create a new backup every time and keep n old backups + return copyWallet(walletFile, backupFile, true); + } + + @Override + public void onWalletBackup(String walletName) { + Log.d(TAG, "backup for wallet ." + walletName + "."); + new AsyncBackup().execute(walletName); + } + + private class AsyncArchive extends AsyncTask { + ProgressDialog progressDialog = new MyProgressDialog(LoginActivity.this, R.string.archive_progress); + + @Override + protected void onPreExecute() { + super.onPreExecute(); + progressDialog.show(); + LoginActivity.this.asyncWaitTask = this; + } + + @Override + protected Boolean doInBackground(String... params) { + if (params.length != 1) return false; + String walletName = params[0]; + if (backupWallet(walletName) && deleteWallet(Helper.getWalletFile(LoginActivity.this, walletName))) { + return true; + } else { + return false; + } + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + progressDialog.dismiss(); + if (result) { + reloadWalletList(); + } else { + Toast.makeText(LoginActivity.this, getString(R.string.archive_failed), Toast.LENGTH_LONG).show(); + } + LoginActivity.this.asyncWaitTask = null; + } + } + + @Override + public void onWalletArchive(final String walletName) { + Log.d(TAG, "archive for wallet ." + walletName + "."); + if (checkServiceRunning()) return; + DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + new AsyncArchive().execute(walletName); + break; + case DialogInterface.BUTTON_NEGATIVE: + // do nothing + break; + } + } + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage(getString(R.string.archive_alert_message)) + .setTitle(walletName) + .setPositiveButton(getString(R.string.archive_alert_yes), dialogClickListener) + .setNegativeButton(getString(R.string.archive_alert_no), dialogClickListener) + .show(); + } + + void reloadWalletList() { + try { + LoginFragment loginFragment = (LoginFragment) + getSupportFragmentManager().findFragmentById(R.id.fragment_container); + loginFragment.loadList(); + } catch (ClassCastException ex) { + } + } + @Override public void onAddWallet() { + if (checkServiceRunning()) return; startGenerateFragment(); } @@ -273,10 +499,64 @@ public class LoginActivity extends AppCompatActivity super.onPause(); } + AsyncTask asyncWaitTask = null; + @Override protected void onResume() { super.onResume(); Log.d(TAG, "onResume()"); + if (WalletService.Running && (asyncWaitTask == null)) { + Log.d(TAG, "new process dialog"); + new AsyncWaitForService().execute(); + } + } + + + private class MyProgressDialog extends ProgressDialog { + Activity activity; + + public MyProgressDialog(Activity activity, int msgId) { + super(activity); + this.activity = activity; + setCancelable(false); + setMessage(activity.getString(msgId)); + } + + @Override + public void onBackPressed() { + //activity.finish(); + } + } + + + private class AsyncWaitForService extends AsyncTask { + ProgressDialog progressDialog = new MyProgressDialog(LoginActivity.this, R.string.service_progress); + + @Override + protected void onPreExecute() { + super.onPreExecute(); + progressDialog.show(); + LoginActivity.this.asyncWaitTask = this; + } + + @Override + protected Void doInBackground(Void... params) { + try { + while (WalletService.Running & !isCancelled()) { + Thread.sleep(250); + } + } catch (InterruptedException ex) { + // oh well ... + } + return null; + } + + @Override + protected void onPostExecute(Void result) { + super.onPostExecute(result); + progressDialog.dismiss(); + LoginActivity.this.asyncWaitTask = null; + } } @@ -403,7 +683,7 @@ public class LoginActivity extends AppCompatActivity Toast.makeText(LoginActivity.this, getString(R.string.generate_wallet_create_failed), Toast.LENGTH_LONG).show(); Log.e(TAG, "Could not create new wallet in " + newWalletFile.getAbsolutePath()); - + genFragment.walletGenerateError(); } } @@ -433,9 +713,12 @@ public class LoginActivity extends AppCompatActivity public boolean createWallet(File aFile, String password) { Wallet newWallet = WalletManager.getInstance().recoveryWallet(aFile, seed, restoreHeight); boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok); - if (!success) Log.e(TAG, newWallet.getErrorString()); - newWallet.setPassword(password); - success = success && newWallet.store(); + if (success) { + newWallet.setPassword(password); + success = success && newWallet.store(); + } else { + Log.e(TAG, newWallet.getErrorString()); + } newWallet.close(); return success; } @@ -452,9 +735,12 @@ public class LoginActivity extends AppCompatActivity .createWalletFromKeys(aFile, MNEMONIC_LANGUAGE, restoreHeight, address, viewKey, spendKey); boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok); - if (!success) Log.e(TAG, newWallet.getErrorString()); - newWallet.setPassword(password); - success = success && newWallet.store(); + if (success) { + newWallet.setPassword(password); + success = success && newWallet.store(); + } else { + Log.e(TAG, newWallet.getErrorString()); + } newWallet.close(); return success; } @@ -464,18 +750,18 @@ public class LoginActivity extends AppCompatActivity @Override public void onAccept(final String name, final String password) { - final File newWalletFolder = new File(getStorageRoot(), ".new"); + final File newWalletFile = new File(new File(getStorageRoot(), ".new"), name); final File walletFolder = getStorageRoot(); - final String walletPath = new File(walletFolder, name).getAbsolutePath(); - final boolean rc = copyWallet(walletFolder, newWalletFolder, name) + final File walletFile = new File(walletFolder, name); + final boolean rc = copyWallet(newWalletFile, walletFile, false) && - (testWallet(walletPath, password) == Wallet.Status.Status_Ok); + (testWallet(walletFile.getAbsolutePath(), password) == Wallet.Status.Status_Ok); if (rc) { popFragmentStack(GENERATE_STACK); Toast.makeText(LoginActivity.this, getString(R.string.generate_wallet_created), Toast.LENGTH_SHORT).show(); } else { - Log.e(TAG, "Wallet store failed to " + walletPath); + Log.e(TAG, "Wallet store failed to " + walletFile.getAbsolutePath()); Toast.makeText(LoginActivity.this, getString(R.string.generate_wallet_create_failed_2), Toast.LENGTH_LONG).show(); } @@ -491,30 +777,58 @@ public class LoginActivity extends AppCompatActivity return status; } - boolean copyWallet(File dstDir, File srcDir, String name) { + boolean walletExists(File walletFile, boolean any) { + File dir = walletFile.getParentFile(); + String name = walletFile.getName(); + if (any) { + return new File(dir, name).exists() + || new File(dir, name + ".keys").exists() + || new File(dir, name + ".address.txt").exists(); + } else { + return new File(dir, name).exists() + && new File(dir, name + ".keys").exists() + && new File(dir, name + ".address.txt").exists(); + } + } + + boolean copyWallet(File srcWallet, File dstWallet, boolean overwrite) { + //Log.d(TAG, "src=" + srcWallet.exists() + " dst=" + dstWallet.exists()); + if (walletExists(dstWallet, true) && !overwrite) return false; + if (!walletExists(srcWallet, false)) return false; + boolean success = false; + File srcDir = srcWallet.getParentFile(); + String srcName = srcWallet.getName(); + File dstDir = dstWallet.getParentFile(); + String dstName = dstWallet.getName(); try { - // the cache is corrupt if we recover (!!) - // the cache is ok if we immediately do a full refresh() - // recoveryheight is ignored but not on watchonly wallet ?! - find out why - // so we just ignore the cache file and rebuild it on first sync - //copyFile(dstDir, srcDir, name); - copyFile(dstDir, srcDir, name + ".keys"); - copyFile(dstDir, srcDir, name + ".address.txt"); + copyFile(new File(srcDir, srcName), new File(dstDir, dstName)); + copyFile(new File(srcDir, srcName + ".keys"), new File(dstDir, dstName + ".keys")); + copyFile(new File(srcDir, srcName + ".address.txt"), new File(dstDir, dstName + ".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(); + deleteWallet(dstWallet); } 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(); + // do our best to delete as much as possible of the wallet files + boolean deleteWallet(File walletFile) { + Log.d(TAG, "deleteWallet " + walletFile.getAbsolutePath()); + if (!walletFile.isFile()) return false; + File dir = walletFile.getParentFile(); + String name = walletFile.getName(); + boolean success = new File(dir, name).delete(); + success = new File(dir, name + ".keys").delete() && success; + success = new File(dir, name + ".address.txt").delete() && success; + return success; + } + + void copyFile(File src, File dst) throws IOException { + FileChannel inChannel = new FileInputStream(src).getChannel(); + FileChannel outChannel = new FileOutputStream(dst).getChannel(); try { inChannel.transferTo(0, inChannel.size(), outChannel); } finally { diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java index 70c6055..3a15f42 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; import android.os.StrictMode; +import android.support.annotation.NonNull; import android.support.design.widget.FloatingActionButton; import android.support.v4.app.Fragment; import android.util.Log; @@ -82,11 +83,17 @@ public class LoginFragment extends Fragment { File getStorageRoot(); - void onWalletSelected(final String wallet); + void onWalletSelected(String wallet); - void onWalletDetails(final String wallet); + void onWalletDetails(String wallet); - void onWalletReceive(final String wallet); + void onWalletReceive(String wallet); + + void onWalletRename(String name); + + void onWalletBackup(String name); + + void onWalletArchive(String walletName); void onAddWallet(); @@ -126,7 +133,7 @@ public class LoginFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - + Log.d(TAG, "onCreateView"); View view = inflater.inflate(R.layout.login_fragment, container, false); tbMainNet = (ToggleButton) view.findViewById(R.id.tbMainNet); @@ -224,31 +231,6 @@ public class LoginFragment extends Fragment { } }); -/* listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { - @Override - public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { - // Difference to opening wallet is that we don't need a daemon set - String itemValue = (String) listView.getItemAtPosition(position); - - if (itemValue.length() <= (WALLETNAME_PREAMBLE_LENGTH)) { - Toast.makeText(getActivity(), getString(R.string.panic), Toast.LENGTH_LONG).show(); - return true; - } - - String wallet = itemValue.substring(WALLETNAME_PREAMBLE_LENGTH); - String x = isMainNet() ? "4" : "9A"; - if (x.indexOf(itemValue.charAt(1)) < 0) { - Toast.makeText(getActivity(), getString(R.string.prompt_wrong_net), Toast.LENGTH_LONG).show(); - return true; - } - - checkAndSetWalletDaemon("", !isMainNet()); // just set selected net - - activityCallback.onWalletDetails(wallet); - return true; - } - }); -*/ loadList(); return view; } @@ -262,7 +244,8 @@ public class LoginFragment extends Fragment { } } - private void loadList() { + public void loadList() { + Log.d(TAG, "loadList()"); WalletManager mgr = WalletManager.getInstance(); List walletInfos = mgr.findWallets(activityCallback.getStorageRoot()); @@ -411,51 +394,51 @@ public class LoginFragment extends Fragment { public boolean onContextItemSelected(MenuItem item) { AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); String listItem = (String) listView.getItemAtPosition(info.position); + String name = nameFromListItem(listItem, !isMainNet()); + if (name == null) { + Toast.makeText(getActivity(), getString(R.string.panic), Toast.LENGTH_LONG).show(); + } switch (item.getItemId()) { case R.id.action_info: - return showInfo(listItem); + showInfo(name); + break; case R.id.action_receive: - return showReceive(listItem); + showReceive(name); + break; + case R.id.action_rename: + activityCallback.onWalletRename(name); + break; + case R.id.action_backup: + activityCallback.onWalletBackup(name); + break; + case R.id.action_archive: + activityCallback.onWalletArchive(name); + break; default: return super.onContextItemSelected(item); } - } - - private boolean showInfo(String listItem) { - if (listItem.length() <= (WALLETNAME_PREAMBLE_LENGTH)) { - Toast.makeText(getActivity(), getString(R.string.panic), Toast.LENGTH_LONG).show(); - return true; - } - - String wallet = listItem.substring(WALLETNAME_PREAMBLE_LENGTH); - String x = isMainNet() ? "4" : "9A"; - if (x.indexOf(listItem.charAt(1)) < 0) { - Toast.makeText(getActivity(), getString(R.string.prompt_wrong_net), Toast.LENGTH_LONG).show(); - return true; - } - - checkAndSetWalletDaemon("", !isMainNet()); // just set selected net - - activityCallback.onWalletDetails(wallet); return true; } - private boolean showReceive(String listItem) { - if (listItem.length() <= (WALLETNAME_PREAMBLE_LENGTH)) { - Toast.makeText(getActivity(), getString(R.string.panic), Toast.LENGTH_LONG).show(); - return true; - } - - String wallet = listItem.substring(WALLETNAME_PREAMBLE_LENGTH); - String x = isMainNet() ? "4" : "9A"; - if (x.indexOf(listItem.charAt(1)) < 0) { - Toast.makeText(getActivity(), getString(R.string.prompt_wrong_net), Toast.LENGTH_LONG).show(); - return true; - } - + private void showInfo(@NonNull String name) { checkAndSetWalletDaemon("", !isMainNet()); // just set selected net - activityCallback.onWalletReceive(wallet); + activityCallback.onWalletDetails(name); + } + + private boolean showReceive(@NonNull String name) { + checkAndSetWalletDaemon("", !isMainNet()); // just set selected net + + activityCallback.onWalletReceive(name); return true; } + + private String nameFromListItem(String listItem, boolean testnet) { + String wallet = listItem.substring(WALLETNAME_PREAMBLE_LENGTH); + String x = testnet ? "9A" : "4"; + if (x.indexOf(listItem.charAt(1)) < 0) { + return null; + } + return wallet; + } } diff --git a/app/src/main/java/com/m2049r/xmrwallet/ReceiveFragment.java b/app/src/main/java/com/m2049r/xmrwallet/ReceiveFragment.java index 49f255b..fe13852 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/ReceiveFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/ReceiveFragment.java @@ -79,8 +79,6 @@ public class ReceiveFragment extends Fragment { etPaymentId.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); - Helper.showKeyboard(getActivity()); - etPaymentId.requestFocus(); etPaymentId.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)) { @@ -114,7 +112,7 @@ public class ReceiveFragment extends Fragment { etAmount.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 ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) { if (paymentIdOk() && amountOk()) { Helper.hideKeyboard(getActivity()); generateQr(); @@ -189,15 +187,18 @@ public class ReceiveFragment extends Fragment { } } - - private void show(String address) { - tvAddress.setText(address); - etPaymentId.setEnabled(true); - etAmount.setEnabled(true); - bPaymentId.setEnabled(true); - bGenerate.setEnabled(true); - hideProgress(); - generateQr(); + private void show(final String address) { + getActivity().runOnUiThread(new Runnable() { + public void run() { + tvAddress.setText(address); + etPaymentId.setEnabled(true); + etAmount.setEnabled(true); + bPaymentId.setEnabled(true); + bGenerate.setEnabled(true); + hideProgress(); + generateQr(); + } + }); } private void show(final String walletPath, final String password) { @@ -208,8 +209,9 @@ public class ReceiveFragment extends Fragment { final Wallet wallet = WalletManager.getInstance().openWallet(walletPath, password); getActivity().runOnUiThread(new Runnable() { public void run() { - show(wallet.getAddress()); + String address = wallet.getAddress(); wallet.close(); + show(address); } }); } @@ -259,6 +261,7 @@ public class ReceiveFragment extends Fragment { etAmount.setText(amount); qrCode.setImageBitmap(qr); etDummy.requestFocus(); + bGenerate.setEnabled(false); } } diff --git a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java index fc9f340..ea0e12a 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java @@ -93,20 +93,21 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment. } private void startWalletService() { - acquireWakeLock(); Bundle extras = getIntent().getExtras(); if (extras != null) { + acquireWakeLock(); String walletId = extras.getString(REQUEST_ID); String walletPassword = extras.getString(REQUEST_PW); connectWalletService(walletId, walletPassword); } else { - throw new IllegalStateException("No extras passed! Panic!"); + finish(); + //throw new IllegalStateException("No extras passed! Panic!"); } } private void stopWalletService() { - releaseWakeLock(); disconnectWalletService(); + releaseWakeLock(); } @Override diff --git a/app/src/main/java/com/m2049r/xmrwallet/model/Wallet.java b/app/src/main/java/com/m2049r/xmrwallet/model/Wallet.java index fde4417..8496953 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/model/Wallet.java +++ b/app/src/main/java/com/m2049r/xmrwallet/model/Wallet.java @@ -238,6 +238,7 @@ public class Wallet { public native void setDefaultMixin(int mixin); public native boolean setUserNote(String txid, String note); + public native String getUserNote(String txid); public native String getTxKey(String txid); diff --git a/app/src/main/res/layout/gen_review_fragment.xml b/app/src/main/res/layout/gen_review_fragment.xml index 56891ac..35fc2b8 100644 --- a/app/src/main/res/layout/gen_review_fragment.xml +++ b/app/src/main/res/layout/gen_review_fragment.xml @@ -137,7 +137,6 @@ android:id="@+id/tvWalletSpendKey" android:layout_width="match_parent" android:layout_height="wrap_content" - android:selectAllOnFocus="true" android:textAlignment="center" android:textColor="@color/colorPrimaryDark" android:textIsSelectable="true" diff --git a/app/src/main/res/layout/prompt_password.xml b/app/src/main/res/layout/prompt_password.xml index 8bd5f7e..495d467 100644 --- a/app/src/main/res/layout/prompt_password.xml +++ b/app/src/main/res/layout/prompt_password.xml @@ -4,14 +4,14 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - android:padding="10dp" > + android:padding="10dp"> + android:inputType="textPassword"> diff --git a/app/src/main/res/layout/prompt_rename.xml b/app/src/main/res/layout/prompt_rename.xml new file mode 100644 index 0000000..5f47af1 --- /dev/null +++ b/app/src/main/res/layout/prompt_rename.xml @@ -0,0 +1,24 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/receive_fragment.xml b/app/src/main/res/layout/receive_fragment.xml index 732cc78..46b4c3f 100644 --- a/app/src/main/res/layout/receive_fragment.xml +++ b/app/src/main/res/layout/receive_fragment.xml @@ -18,6 +18,8 @@ android:layout_height="wrap_content" android:layout_marginBottom="4sp" android:layout_marginTop="4sp" + android:textIsSelectable="true" + android:selectAllOnFocus="true" android:hint="@string/send_address_hint" android:textAlignment="center" android:textSize="16sp" /> @@ -88,7 +90,7 @@ android:layout_weight="7" android:enabled="false" android:hint="@string/receive_amount_hint" - android:imeOptions="actionNext" + android:imeOptions="actionDone" android:inputType="numberDecimal" android:textAlignment="textStart" android:textSize="24sp" /> diff --git a/app/src/main/res/menu/list_menu.xml b/app/src/main/res/menu/list_menu.xml index ecc5457..d52b7a9 100644 --- a/app/src/main/res/menu/list_menu.xml +++ b/app/src/main/res/menu/list_menu.xml @@ -3,10 +3,24 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c6acb10..d93c00b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4,7 +4,23 @@ Wallet Details - Receive + QR Receive + Rename + Archive + Backup + + Backup in progress + Archive in progress + Rename in progress + + Wrapping things up …\nThis can take a while! + + Backup successful + Backup failed! + Archive successful + Archive failed! + Delete failed! + Rename failed! [<user>:<pass>@]<daemon>[:<port>] Net Selection @@ -25,6 +41,8 @@ I am still busy with your last wallet … + Rename + Password for Bad password! Wallet does not exists! @@ -77,6 +95,7 @@ <Watch Only Wallet> Wallet exists! Choose another name + Wallet name may not begin with \'.\' Wallet created Wallet create failed Wallet create failed (1/2) @@ -163,6 +182,10 @@ I\'m safe Take me back! + The wallet will be backuped up and then deleted! + Yes, do that! + No thanks! + 999999.999999999999 diff --git a/xmrwallet.iml b/xmrwallet.iml index a36ea9c..bb050e8 100644 --- a/xmrwallet.iml +++ b/xmrwallet.iml @@ -1,5 +1,5 @@ - +