From c115109942eb822240dce8954f23ed0067f5de1b Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 19 Apr 2018 10:06:37 +0800 Subject: [PATCH 01/12] add nfc backup ui --- .../com/m2049r/xmrwallet/LoginActivity.java | 2002 +++++++++-------- .../com/m2049r/xmrwallet/LoginFragment.java | 12 +- app/src/main/res/menu/list_context_menu.xml | 8 +- app/src/main/res/values-es/strings.xml | 4 +- app/src/main/res/values/strings.xml | 2 + 5 files changed, 1029 insertions(+), 999 deletions(-) diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java index 50d1d8825c..6f2aa5ce90 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java @@ -71,314 +71,189 @@ public class LoginActivity extends SecureActivity implements LoginFragment.Listener, GenerateFragment.Listener, GenerateReviewFragment.Listener, GenerateReviewFragment.AcceptListener, ReceiveFragment.Listener { - private static final String GENERATE_STACK = "gen"; + private static final String GENERATE_STACK = "gen"; - static final int DAEMON_TIMEOUT = 500; // deamon must respond in 500ms + static final int DAEMON_TIMEOUT = 500; // deamon must respond in 500ms - private Toolbar toolbar; + private Toolbar toolbar; - @Override - public void setToolbarButton(int type) { - toolbar.setButton(type); - } - - @Override - public void setTitle(String title) { - toolbar.setTitle(title); - } - - @Override - public void setSubtitle(String subtitle) { - toolbar.setSubtitle(subtitle); - } + @Override + public void setToolbarButton(int type) { + toolbar.setButton(type); + } - @Override - public void setTitle(String title, String subtitle) { - toolbar.setTitle(title, subtitle); - } + @Override + public void setTitle(String title) { + toolbar.setTitle(title); + } - @Override - protected void onCreate(Bundle savedInstanceState) { - Timber.d("onCreate()"); - super.onCreate(savedInstanceState); - if (savedInstanceState != null) { - // we don't store anything ourselves + @Override + public void setSubtitle(String subtitle) { + toolbar.setSubtitle(subtitle); } - setContentView(R.layout.activity_login); - toolbar = (Toolbar) findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - getSupportActionBar().setDisplayShowTitleEnabled(false); + @Override + public void setTitle(String title, String subtitle) { + toolbar.setTitle(title, subtitle); + } - toolbar.setOnButtonListener(new Toolbar.OnButtonListener() { - @Override - public void onButton(int type) { - switch (type) { - case Toolbar.BUTTON_BACK: - onBackPressed(); - break; - case Toolbar.BUTTON_CLOSE: - finish(); - break; - case Toolbar.BUTTON_DONATE: - DonationFragment.display(getSupportFragmentManager()); - break; - case Toolbar.BUTTON_NONE: - default: - Timber.e("Button " + type + "pressed - how can this be?"); - } + @Override + protected void onCreate(Bundle savedInstanceState) { + Timber.d("onCreate()"); + super.onCreate(savedInstanceState); + if (savedInstanceState != null) { + // we don't store anything ourselves } - }); - if (Helper.getWritePermission(this)) { - if (savedInstanceState == null) startLoginFragment(); - } else { - Timber.i("Waiting for permissions"); - } - } + setContentView(R.layout.activity_login); + toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayShowTitleEnabled(false); - boolean checkServiceRunning() { - if (WalletService.Running) { - Toast.makeText(this, getString(R.string.service_busy), Toast.LENGTH_SHORT).show(); - return true; - } else { - return false; - } - } - - @Override - public boolean onWalletSelected(String walletName, String daemon, boolean testnet) { - if (daemon.length() == 0) { - Toast.makeText(this, getString(R.string.prompt_daemon_missing), Toast.LENGTH_SHORT).show(); - return false; - } - if (checkServiceRunning()) return false; - try { - WalletNode aWalletNode = new WalletNode(walletName, daemon, testnet); - new AsyncOpenWallet().execute(aWalletNode); - } catch (IllegalArgumentException ex) { - Timber.e(ex.getLocalizedMessage()); - Toast.makeText(this, ex.getLocalizedMessage(), Toast.LENGTH_SHORT).show(); - return false; - } - return true; - } - - @Override - public void onWalletDetails(final String walletName, boolean testnet) { - setNet(testnet); - Timber.d("details 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: - final File walletFile = Helper.getWalletFile(LoginActivity.this, walletName); - if (WalletManager.getInstance().walletExists(walletFile)) { - promptPassword(walletName, new PasswordAction() { - @Override - public void action(String walletName, String password) { - startDetails(walletFile, password, GenerateReviewFragment.VIEW_TYPE_DETAILS); - } - }); - } else { // this cannot really happen as we prefilter choices - Timber.e("Wallet missing: %s", walletName); - Toast.makeText(LoginActivity.this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show(); - } - break; - - case DialogInterface.BUTTON_NEGATIVE: - // do nothing - break; - } - } - }; - - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setMessage(getString(R.string.details_alert_message)) - .setPositiveButton(getString(R.string.details_alert_yes), dialogClickListener) - .setNegativeButton(getString(R.string.details_alert_no), dialogClickListener) - .show(); - } - - @Override - public void onWalletReceive(String walletName, boolean testnet) { - setNet(testnet); - Timber.d("receive for wallet ." + walletName + "."); - if (checkServiceRunning()) return; - final File walletFile = Helper.getWalletFile(this, walletName); - if (WalletManager.getInstance().walletExists(walletFile)) { - promptPassword(walletName, new PasswordAction() { + toolbar.setOnButtonListener(new Toolbar.OnButtonListener() { @Override - public void action(String walletName, String password) { - startReceive(walletFile, password); + public void onButton(int type) { + switch (type) { + case Toolbar.BUTTON_BACK: + onBackPressed(); + break; + case Toolbar.BUTTON_CLOSE: + finish(); + break; + case Toolbar.BUTTON_DONATE: + DonationFragment.display(getSupportFragmentManager()); + break; + case Toolbar.BUTTON_NONE: + default: + Timber.e("Button " + type + "pressed - how can this be?"); + } } }); - } else { // this cannot really happen as we prefilter choices - Toast.makeText(this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show(); - } - } - private class AsyncRename extends AsyncTask { - @Override - protected void onPreExecute() { - super.onPreExecute(); - showProgressDialog(R.string.rename_progress); + if (Helper.getWritePermission(this)) { + if (savedInstanceState == null) startLoginFragment(); + } else { + Timber.i("Waiting for permissions"); + } } - @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); + boolean checkServiceRunning() { + if (WalletService.Running) { + Toast.makeText(this, getString(R.string.service_busy), Toast.LENGTH_SHORT).show(); + return true; + } else { + return false; + } } @Override - protected void onPostExecute(Boolean result) { - super.onPostExecute(result); - if (isDestroyed()) { - return; + public boolean onWalletSelected(String walletName, String daemon, boolean testnet) { + if (daemon.length() == 0) { + Toast.makeText(this, getString(R.string.prompt_daemon_missing), Toast.LENGTH_SHORT).show(); + return false; } - dismissProgressDialog(); - if (result) { - reloadWalletList(); - } else { - Toast.makeText(LoginActivity.this, getString(R.string.rename_failed), Toast.LENGTH_LONG).show(); + if (checkServiceRunning()) return false; + try { + WalletNode aWalletNode = new WalletNode(walletName, daemon, testnet); + new AsyncOpenWallet().execute(aWalletNode); + } catch (IllegalArgumentException ex) { + Timber.e(ex.getLocalizedMessage()); + Toast.makeText(this, ex.getLocalizedMessage(), Toast.LENGTH_SHORT).show(); + return false; } - } - } - - // copy + delete seems safer than rename because we call rollback easily - boolean renameWallet(File walletFile, String newName) { - if (copyWallet(walletFile, new File(walletFile.getParentFile(), newName), false, true)) { - deleteWallet(walletFile); return true; - } else { - return false; } - } - - @Override - public void onWalletRename(final String walletName) { - Timber.d("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(getString(R.string.label_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(getString(R.string.label_cancel), - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - Helper.hideKeyboardAlways(LoginActivity.this); - dialog.cancel(); + @Override + public void onWalletDetails(final String walletName, boolean testnet) { + setNet(testnet); + Timber.d("details 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: + final File walletFile = Helper.getWalletFile(LoginActivity.this, walletName); + if (WalletManager.getInstance().walletExists(walletFile)) { + promptPassword(walletName, new PasswordAction() { + @Override + public void action(String walletName, String password) { + startDetails(walletFile, password, GenerateReviewFragment.VIEW_TYPE_DETAILS); + } + }); + } else { // this cannot really happen as we prefilter choices + Timber.e("Wallet missing: %s", walletName); + Toast.makeText(LoginActivity.this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show(); } - }); - - 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; - } - }); + break; - dialog.show(); - } + case DialogInterface.BUTTON_NEGATIVE: + // do nothing + break; + } + } + }; - private class AsyncBackup extends AsyncTask { - @Override - protected void onPreExecute() { - super.onPreExecute(); - showProgressDialog(R.string.backup_progress); + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage(getString(R.string.details_alert_message)) + .setPositiveButton(getString(R.string.details_alert_yes), dialogClickListener) + .setNegativeButton(getString(R.string.details_alert_no), dialogClickListener) + .show(); } @Override - protected Boolean doInBackground(String... params) { - if (params.length != 1) return false; - return backupWallet(params[0]); + public void onWalletReceive(String walletName, boolean testnet) { + setNet(testnet); + Timber.d("receive for wallet ." + walletName + "."); + if (checkServiceRunning()) return; + final File walletFile = Helper.getWalletFile(this, walletName); + if (WalletManager.getInstance().walletExists(walletFile)) { + promptPassword(walletName, new PasswordAction() { + @Override + public void action(String walletName, String password) { + startReceive(walletFile, password); + } + }); + } else { // this cannot really happen as we prefilter choices + Toast.makeText(this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show(); + } } - @Override - protected void onPostExecute(Boolean result) { - super.onPostExecute(result); - if (isDestroyed()) { - return; + private class AsyncRename extends AsyncTask { + @Override + protected void onPreExecute() { + super.onPreExecute(); + showProgressDialog(R.string.rename_progress); } - dismissProgressDialog(); - if (!result) { - Toast.makeText(LoginActivity.this, getString(R.string.backup_failed), Toast.LENGTH_LONG).show(); + + @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); } - } - } - private boolean backupWallet(String walletName) { - File backupFolder = new File(getStorageRoot(), "backups"); - if (!backupFolder.exists()) { - if (!backupFolder.mkdir()) { - Timber.e("Cannot create backup dir %s", backupFolder.getAbsolutePath()); - return false; + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + if (isDestroyed()) { + return; + } + dismissProgressDialog(); + if (result) { + reloadWalletList(); + } else { + Toast.makeText(LoginActivity.this, getString(R.string.rename_failed), Toast.LENGTH_LONG).show(); + } } - // make folder visible over USB/MTP - MediaScannerConnection.scanFile(this, new String[]{backupFolder.toString()}, null, null); - } - File walletFile = Helper.getWalletFile(LoginActivity.this, walletName); - File backupFile = new File(backupFolder, walletName); - Timber.d("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 - boolean success = copyWallet(walletFile, backupFile, true, true); - Timber.d("copyWallet is %s", success); - return success; - } - - @Override - public void onWalletBackup(String walletName) { - Timber.d("backup for wallet ." + walletName + "."); - new AsyncBackup().execute(walletName); - } - - private class AsyncArchive extends AsyncTask { - @Override - protected void onPreExecute() { - super.onPreExecute(); - showProgressDialog(R.string.archive_progress); } - @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))) { + // copy + delete seems safer than rename because we call rollback easily + boolean renameWallet(File walletFile, String newName) { + if (copyWallet(walletFile, new File(walletFile.getParentFile(), newName), false, true)) { + deleteWallet(walletFile); return true; } else { return false; @@ -386,120 +261,278 @@ protected Boolean doInBackground(String... params) { } @Override - protected void onPostExecute(Boolean result) { - super.onPostExecute(result); - if (isDestroyed()) { - return; + public void onWalletRename(final String walletName) { + Timber.d("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(getString(R.string.label_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(getString(R.string.label_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 AsyncBackupToFile extends AsyncTask { + @Override + protected void onPreExecute() { + super.onPreExecute(); + showProgressDialog(R.string.backup_progress); } - dismissProgressDialog(); - if (result) { - reloadWalletList(); - } else { - Toast.makeText(LoginActivity.this, getString(R.string.archive_failed), Toast.LENGTH_LONG).show(); + + @Override + protected Boolean doInBackground(String... params) { + if (params.length != 1) return false; + return backupWallet(params[0]); } - } - } - @Override - public void onWalletArchive(final String walletName) { - Timber.d("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; + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + if (isDestroyed()) { + return; + } + dismissProgressDialog(); + if (!result) { + Toast.makeText(LoginActivity.this, getString(R.string.backup_failed), Toast.LENGTH_LONG).show(); } } - }; - - 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() { - Timber.d("reloadWalletList()"); - try { - LoginFragment loginFragment = (LoginFragment) - getSupportFragmentManager().findFragmentById(R.id.fragment_container); - if (loginFragment != null) { - loginFragment.loadList(); - } - } catch (ClassCastException ex) { } - } - @Override - public void onAddWallet(boolean testnet, String type) { - setNet(testnet); - if (checkServiceRunning()) return; - startGenerateFragment(type); - } + private boolean backupWallet(String walletName) { + File backupFolder = new File(getStorageRoot(), "backups"); + if (!backupFolder.exists()) { + if (!backupFolder.mkdir()) { + Timber.e("Cannot create backup dir %s", backupFolder.getAbsolutePath()); + return false; + } + // make folder visible over USB/MTP + MediaScannerConnection.scanFile(this, new String[]{backupFolder.toString()}, null, null); + } + File walletFile = Helper.getWalletFile(LoginActivity.this, walletName); + File backupFile = new File(backupFolder, walletName); + Timber.d("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 + boolean success = copyWallet(walletFile, backupFile, true, true); + Timber.d("copyWallet is %s", success); + return success; + } - AlertDialog passwordDialog = null; // for preventing multiple clicks in wallet list + /* + modified into onWalletBackupToFile + @Override + public void onWalletBackup(String walletName) { + Timber.d("backup for wallet ." + walletName + "."); + new AsyncBackup().execute(walletName); + }*/ - void promptPassword(final String wallet, final PasswordAction action) { - if (passwordDialog != null) return; // we are already asking for password - Context context = LoginActivity.this; - LayoutInflater li = LayoutInflater.from(context); - View promptsView = li.inflate(R.layout.prompt_password, null); + @Override + public void onWalletBackupToFile(String walletName) { + Timber.d("backup to file for wallet ." + walletName + "."); + new AsyncBackupToFile().execute(walletName); + } - AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context); - alertDialogBuilder.setView(promptsView); + @Override + public void onWalletBackupToNFC(String walletName) { + Timber.d("backup to nfc for wallet ." + walletName + "."); - final TextInputLayout etPassword = (TextInputLayout) promptsView.findViewById(R.id.etPassword); - etPassword.setHint(LoginActivity.this.getString(R.string.prompt_password, wallet)); + } - etPassword.getEditText().addTextChangedListener(new TextWatcher() { + private class AsyncArchive extends AsyncTask { + @Override + protected void onPreExecute() { + super.onPreExecute(); + showProgressDialog(R.string.archive_progress); + } @Override - public void afterTextChanged(Editable s) { - if (etPassword.getError() != null) { - etPassword.setError(null); + 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 - public void beforeTextChanged(CharSequence s, int start, - int count, int after) { + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + if (isDestroyed()) { + return; + } + dismissProgressDialog(); + if (result) { + reloadWalletList(); + } else { + Toast.makeText(LoginActivity.this, getString(R.string.archive_failed), Toast.LENGTH_LONG).show(); + } } + } - @Override - public void onTextChanged(CharSequence s, int start, - int before, int count) { + @Override + public void onWalletArchive(final String walletName) { + Timber.d("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() { + Timber.d("reloadWalletList()"); + try { + LoginFragment loginFragment = (LoginFragment) + getSupportFragmentManager().findFragmentById(R.id.fragment_container); + if (loginFragment != null) { + loginFragment.loadList(); + } + } catch (ClassCastException ex) { } - }); - - // set dialog message - alertDialogBuilder - .setCancelable(false) - .setPositiveButton(getString(R.string.label_ok), null) - .setNegativeButton(getString(R.string.label_cancel), - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { + } + + @Override + public void onAddWallet(boolean testnet, String type) { + setNet(testnet); + if (checkServiceRunning()) return; + startGenerateFragment(type); + } + + AlertDialog passwordDialog = null; // for preventing multiple clicks in wallet list + + void promptPassword(final String wallet, final PasswordAction action) { + if (passwordDialog != null) return; // we are already asking for password + Context context = LoginActivity.this; + LayoutInflater li = LayoutInflater.from(context); + View promptsView = li.inflate(R.layout.prompt_password, null); + + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context); + alertDialogBuilder.setView(promptsView); + + final TextInputLayout etPassword = (TextInputLayout) promptsView.findViewById(R.id.etPassword); + etPassword.setHint(LoginActivity.this.getString(R.string.prompt_password, wallet)); + + etPassword.getEditText().addTextChangedListener(new TextWatcher() { + + @Override + public void afterTextChanged(Editable s) { + if (etPassword.getError() != null) { + etPassword.setError(null); + } + } + + @Override + public void beforeTextChanged(CharSequence s, int start, + int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, + int before, int count) { + } + }); + + // set dialog message + alertDialogBuilder + .setCancelable(false) + .setPositiveButton(getString(R.string.label_ok), null) + .setNegativeButton(getString(R.string.label_cancel), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + Helper.hideKeyboardAlways(LoginActivity.this); + dialog.cancel(); + passwordDialog = null; + } + }); + passwordDialog = alertDialogBuilder.create(); + + passwordDialog.setOnShowListener(new DialogInterface.OnShowListener() { + @Override + public void onShow(DialogInterface dialog) { + Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE); + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + String pass = etPassword.getEditText().getText().toString(); + if (processPasswordEntry(wallet, pass, action)) { Helper.hideKeyboardAlways(LoginActivity.this); - dialog.cancel(); + passwordDialog.dismiss(); passwordDialog = null; + } else { + etPassword.setError(getString(R.string.bad_password)); } - }); - passwordDialog = alertDialogBuilder.create(); + } + }); + } + }); - passwordDialog.setOnShowListener(new DialogInterface.OnShowListener() { - @Override - public void onShow(DialogInterface dialog) { - Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE); - button.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { + Helper.showKeyboard(passwordDialog); + + // accept keyboard "ok" + etPassword.getEditText().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)) { String pass = etPassword.getEditText().getText().toString(); if (processPasswordEntry(wallet, pass, action)) { Helper.hideKeyboardAlways(LoginActivity.this); @@ -508,724 +541,705 @@ public void onClick(View view) { } else { etPassword.setError(getString(R.string.bad_password)); } + return true; } - }); - } - }); + return false; + } + }); - Helper.showKeyboard(passwordDialog); + passwordDialog.show(); + } - // accept keyboard "ok" - etPassword.getEditText().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)) { - String pass = etPassword.getEditText().getText().toString(); - if (processPasswordEntry(wallet, pass, action)) { - Helper.hideKeyboardAlways(LoginActivity.this); - passwordDialog.dismiss(); - passwordDialog = null; - } else { - etPassword.setError(getString(R.string.bad_password)); - } - return true; - } + private boolean checkWalletPassword(String walletName, String password) { + String walletPath = new File(Helper.getStorageRoot(getApplicationContext()), + walletName + ".keys").getAbsolutePath(); + // only test view key + return WalletManager.getInstance().verifyWalletPassword(walletPath, password, true); + } + + interface PasswordAction { + void action(String walletName, String password); + } + + private boolean processPasswordEntry(String walletName, String pass, PasswordAction action) { + if (checkWalletPassword(walletName, pass)) { + action.action(walletName, pass); + return true; + } else { return false; } - }); + } - passwordDialog.show(); - } + //////////////////////////////////////// + // LoginFragment.Listener + //////////////////////////////////////// + @Override + public SharedPreferences getPrefs() { + return getPreferences(Context.MODE_PRIVATE); + } - private boolean checkWalletPassword(String walletName, String password) { - String walletPath = new File(Helper.getStorageRoot(getApplicationContext()), - walletName + ".keys").getAbsolutePath(); - // only test view key - return WalletManager.getInstance().verifyWalletPassword(walletPath, password, true); - } + @Override + public File getStorageRoot() { + return Helper.getStorageRoot(getApplicationContext()); + } - interface PasswordAction { - void action(String walletName, String password); - } + //////////////////////////////////////// + //////////////////////////////////////// - private boolean processPasswordEntry(String walletName, String pass, PasswordAction action) { - if (checkWalletPassword(walletName, pass)) { - action.action(walletName, pass); - return true; - } else { - return false; - } - } - - //////////////////////////////////////// - // LoginFragment.Listener - //////////////////////////////////////// - @Override - public SharedPreferences getPrefs() { - return getPreferences(Context.MODE_PRIVATE); - } - - @Override - public File getStorageRoot() { - return Helper.getStorageRoot(getApplicationContext()); - } - - //////////////////////////////////////// - //////////////////////////////////////// - - @Override - public void showNet(boolean testnet) { - if (testnet) { - toolbar.setBackgroundResource(R.color.colorPrimaryDark); - } else { - toolbar.setBackgroundResource(R.drawable.backgound_toolbar_mainnet); - } - toolbar.setSubtitle(getString(testnet ? R.string.connect_testnet : R.string.connect_mainnet)); - } - - @Override - protected void onPause() { - Timber.d("onPause()"); - super.onPause(); - } - - ProgressDialog progressDialog = null; - - private void showProgressDialog(int msgId) { - showProgressDialog(msgId, 0); - } - - private void showProgressDialog(int msgId, long delay) { - dismissProgressDialog(); // just in case - progressDialog = new MyProgressDialog(LoginActivity.this, msgId); - if (delay > 0) { - Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - public void run() { - if (progressDialog != null) progressDialog.show(); - } - }, delay); - } else { - progressDialog.show(); + @Override + public void showNet(boolean testnet) { + if (testnet) { + toolbar.setBackgroundResource(R.color.colorPrimaryDark); + } else { + toolbar.setBackgroundResource(R.drawable.backgound_toolbar_mainnet); + } + toolbar.setSubtitle(getString(testnet ? R.string.connect_testnet : R.string.connect_mainnet)); } - } - private void dismissProgressDialog() { - if (progressDialog != null && progressDialog.isShowing()) { - progressDialog.dismiss(); + @Override + protected void onPause() { + Timber.d("onPause()"); + super.onPause(); } - progressDialog = null; - } - @Override - protected void onDestroy() { - dismissProgressDialog(); - super.onDestroy(); - } + ProgressDialog progressDialog = null; - @Override - protected void onResume() { - super.onResume(); - Timber.d("onResume()"); - // wait for WalletService to finish - if (WalletService.Running && (progressDialog == null)) { - // and show a progress dialog, but only if there isn't one already - new AsyncWaitForService().execute(); + private void showProgressDialog(int msgId) { + showProgressDialog(msgId, 0); } - } - private class MyProgressDialog extends ProgressDialog { - Activity activity; + private void showProgressDialog(int msgId, long delay) { + dismissProgressDialog(); // just in case + progressDialog = new MyProgressDialog(LoginActivity.this, msgId); + if (delay > 0) { + Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + public void run() { + if (progressDialog != null) progressDialog.show(); + } + }, delay); + } else { + progressDialog.show(); + } + } - public MyProgressDialog(Activity activity, int msgId) { - super(activity); - this.activity = activity; - setCancelable(false); - setMessage(activity.getString(msgId)); + private void dismissProgressDialog() { + if (progressDialog != null && progressDialog.isShowing()) { + progressDialog.dismiss(); + } + progressDialog = null; } @Override - public void onBackPressed() { - // prevent back button + protected void onDestroy() { + dismissProgressDialog(); + super.onDestroy(); } - } - - private class AsyncWaitForService extends AsyncTask { @Override - protected void onPreExecute() { - super.onPreExecute(); - showProgressDialog(R.string.service_progress); + protected void onResume() { + super.onResume(); + Timber.d("onResume()"); + // wait for WalletService to finish + if (WalletService.Running && (progressDialog == null)) { + // and show a progress dialog, but only if there isn't one already + new AsyncWaitForService().execute(); + } } - @Override - protected Void doInBackground(Void... params) { - try { - while (WalletService.Running & !isCancelled()) { - Thread.sleep(250); + 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() { + // prevent back button + } + } + + + private class AsyncWaitForService extends AsyncTask { + @Override + protected void onPreExecute() { + super.onPreExecute(); + showProgressDialog(R.string.service_progress); + } + + @Override + protected Void doInBackground(Void... params) { + try { + while (WalletService.Running & !isCancelled()) { + Thread.sleep(250); + } + } catch (InterruptedException ex) { + // oh well ... } - } catch (InterruptedException ex) { - // oh well ... + return null; } - return null; + + @Override + protected void onPostExecute(Void result) { + super.onPostExecute(result); + if (isDestroyed()) { + return; + } + dismissProgressDialog(); + } + } + + void startWallet(String walletName, String walletPassword) { + Timber.d("startWallet()"); + Intent intent = new Intent(getApplicationContext(), WalletActivity.class); + intent.putExtra(WalletActivity.REQUEST_ID, walletName); + intent.putExtra(WalletActivity.REQUEST_PW, walletPassword); + startActivity(intent); + } + + void startDetails(File walletFile, String password, String type) { + Timber.d("startDetails()"); + Bundle b = new Bundle(); + b.putString("path", walletFile.getAbsolutePath()); + b.putString("password", password); + b.putString("type", type); + startReviewFragment(b); + } + + void startReceive(File walletFile, String password) { + Timber.d("startReceive()"); + Bundle b = new Bundle(); + b.putString("path", walletFile.getAbsolutePath()); + b.putString("password", password); + startReceiveFragment(b); } @Override - protected void onPostExecute(Void result) { - super.onPostExecute(result); - if (isDestroyed()) { - return; + public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { + Timber.d("onRequestPermissionsResult()"); + switch (requestCode) { + case Helper.PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE: + // If request is cancelled, the result arrays are empty. + if (grantResults.length > 0 + && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + startLoginFragment = true; + } else { + String msg = getString(R.string.message_strorage_not_permitted); + Timber.e(msg); + Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); + } + break; + default: } - dismissProgressDialog(); } - } - - void startWallet(String walletName, String walletPassword) { - Timber.d("startWallet()"); - Intent intent = new Intent(getApplicationContext(), WalletActivity.class); - intent.putExtra(WalletActivity.REQUEST_ID, walletName); - intent.putExtra(WalletActivity.REQUEST_PW, walletPassword); - startActivity(intent); - } - - void startDetails(File walletFile, String password, String type) { - Timber.d("startDetails()"); - Bundle b = new Bundle(); - b.putString("path", walletFile.getAbsolutePath()); - b.putString("password", password); - b.putString("type", type); - startReviewFragment(b); - } - - void startReceive(File walletFile, String password) { - Timber.d("startReceive()"); - Bundle b = new Bundle(); - b.putString("path", walletFile.getAbsolutePath()); - b.putString("password", password); - startReceiveFragment(b); - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { - Timber.d("onRequestPermissionsResult()"); - switch (requestCode) { - case Helper.PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE: - // If request is cancelled, the result arrays are empty. - if (grantResults.length > 0 - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - startLoginFragment = true; + + private boolean startLoginFragment = false; + + @Override + protected void onResumeFragments() { + super.onResumeFragments(); + if (startLoginFragment) { + startLoginFragment(); + startLoginFragment = false; + } + } + + void startLoginFragment() { + Fragment fragment = new LoginFragment(); + getSupportFragmentManager().beginTransaction() + .add(R.id.fragment_container, fragment).commit(); + Timber.d("LoginFragment added"); + } + + void startGenerateFragment(String type) { + Bundle extras = new Bundle(); + extras.putString(GenerateFragment.TYPE, type); + replaceFragment(new GenerateFragment(), GENERATE_STACK, extras); + Timber.d("GenerateFragment placed"); + } + + void startReviewFragment(Bundle extras) { + replaceFragment(new GenerateReviewFragment(), null, extras); + Timber.d("GenerateReviewFragment placed"); + } + + void startReceiveFragment(Bundle extras) { + replaceFragment(new ReceiveFragment(), null, extras); + Timber.d("ReceiveFragment placed"); + } + + void replaceFragment(Fragment newFragment, String stackName, Bundle extras) { + if (extras != null) { + newFragment.setArguments(extras); + } + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.fragment_container, newFragment); + transaction.addToBackStack(stackName); + transaction.commit(); + } + + void popFragmentStack(String name) { + getSupportFragmentManager().popBackStack(name, FragmentManager.POP_BACK_STACK_INCLUSIVE); + } + + ////////////////////////////////////////// + // GenerateFragment.Listener + ////////////////////////////////////////// + static final String MNEMONIC_LANGUAGE = "English"; // see mnemonics/electrum-words.cpp for more + + private class AsyncCreateWallet extends AsyncTask { + final String walletName; + final String walletPassword; + final WalletCreator walletCreator; + + File newWalletFile; + + public AsyncCreateWallet(final String name, final String password, + final WalletCreator walletCreator) { + super(); + this.walletName = name; + this.walletPassword = password; + this.walletCreator = walletCreator; + } + + @Override + protected void onPreExecute() { + super.onPreExecute(); + showProgressDialog(R.string.generate_wallet_creating); + } + + @Override + protected Boolean doInBackground(Void... params) { + // check if the wallet we want to create already exists + File walletFolder = getStorageRoot(); + if (!walletFolder.isDirectory()) { + Timber.e("Wallet dir " + walletFolder.getAbsolutePath() + "is not a directory"); + return false; + } + File cacheFile = new File(walletFolder, walletName); + File keysFile = new File(walletFolder, walletName + ".keys"); + File addressFile = new File(walletFolder, walletName + ".address.txt"); + + if (cacheFile.exists() || keysFile.exists() || addressFile.exists()) { + Timber.e("Some wallet files already exist for %s", cacheFile.getAbsolutePath()); + return false; + } + + newWalletFile = new File(walletFolder, walletName); + boolean success = walletCreator.createWallet(newWalletFile, walletPassword); + if (success) { + return true; } else { - String msg = getString(R.string.message_strorage_not_permitted); - Timber.e(msg); - Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); + Timber.e("Could not create new wallet in %s", newWalletFile.getAbsolutePath()); + return false; } - break; - default: - } - } - - private boolean startLoginFragment = false; - - @Override - protected void onResumeFragments() { - super.onResumeFragments(); - if (startLoginFragment) { - startLoginFragment(); - startLoginFragment = false; - } - } - - void startLoginFragment() { - Fragment fragment = new LoginFragment(); - getSupportFragmentManager().beginTransaction() - .add(R.id.fragment_container, fragment).commit(); - Timber.d("LoginFragment added"); - } - - void startGenerateFragment(String type) { - Bundle extras = new Bundle(); - extras.putString(GenerateFragment.TYPE, type); - replaceFragment(new GenerateFragment(), GENERATE_STACK, extras); - Timber.d("GenerateFragment placed"); - } - - void startReviewFragment(Bundle extras) { - replaceFragment(new GenerateReviewFragment(), null, extras); - Timber.d("GenerateReviewFragment placed"); - } - - void startReceiveFragment(Bundle extras) { - replaceFragment(new ReceiveFragment(), null, extras); - Timber.d("ReceiveFragment placed"); - } - - void replaceFragment(Fragment newFragment, String stackName, Bundle extras) { - if (extras != null) { - newFragment.setArguments(extras); - } - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.fragment_container, newFragment); - transaction.addToBackStack(stackName); - transaction.commit(); - } - - void popFragmentStack(String name) { - getSupportFragmentManager().popBackStack(name, FragmentManager.POP_BACK_STACK_INCLUSIVE); - } - - ////////////////////////////////////////// - // GenerateFragment.Listener - ////////////////////////////////////////// - static final String MNEMONIC_LANGUAGE = "English"; // see mnemonics/electrum-words.cpp for more - - private class AsyncCreateWallet extends AsyncTask { - final String walletName; - final String walletPassword; - final WalletCreator walletCreator; - - File newWalletFile; - - public AsyncCreateWallet(final String name, final String password, + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + if (isDestroyed()) { + return; + } + dismissProgressDialog(); + if (result) { + startDetails(newWalletFile, walletPassword, GenerateReviewFragment.VIEW_TYPE_ACCEPT); + } else { + walletGenerateError(); + } + } + } + + public void createWallet(final String name, final String password, final WalletCreator walletCreator) { - super(); - this.walletName = name; - this.walletPassword = password; - this.walletCreator = walletCreator; + new AsyncCreateWallet(name, password, walletCreator) + .executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR); + } + + void walletGenerateError() { + try { + GenerateFragment genFragment = (GenerateFragment) + getSupportFragmentManager().findFragmentById(R.id.fragment_container); + genFragment.walletGenerateError(); + } catch (ClassCastException ex) { + Timber.e("walletGenerateError() but not in GenerateFragment"); + } + } + + interface WalletCreator { + boolean createWallet(File aFile, String password); + } @Override - protected void onPreExecute() { - super.onPreExecute(); - showProgressDialog(R.string.generate_wallet_creating); + public void onGenerate(final String name, final String password) { + createWallet(name, password, + new WalletCreator() { + public boolean createWallet(File aFile, String password) { + Wallet newWallet = WalletManager.getInstance() + .createWallet(aFile, password, MNEMONIC_LANGUAGE); + boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok); + if (!success) { + Timber.e(newWallet.getErrorString()); + toast(newWallet.getErrorString()); + } + newWallet.close(); + return success; + } + }); } @Override - protected Boolean doInBackground(Void... params) { - // check if the wallet we want to create already exists + public void onGenerate(final String name, final String password, final String seed, + final long restoreHeight) { + createWallet(name, password, + new WalletCreator() { + public boolean createWallet(File aFile, String password) { + Wallet newWallet = WalletManager.getInstance(). + recoveryWallet(aFile, password, seed, restoreHeight); + boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok); + if (!success) { + Timber.e(newWallet.getErrorString()); + toast(newWallet.getErrorString()); + } + newWallet.close(); + return success; + } + }); + } + + @Override + public void onGenerate(final String name, final String password, + final String address, final String viewKey, final String spendKey, + final long restoreHeight) { + createWallet(name, password, + new WalletCreator() { + public boolean createWallet(File aFile, String password) { + Wallet newWallet = WalletManager.getInstance() + .createWalletWithKeys(aFile, password, MNEMONIC_LANGUAGE, restoreHeight, + address, viewKey, spendKey); + boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok); + if (!success) { + Timber.e(newWallet.getErrorString()); + toast(newWallet.getErrorString()); + } + newWallet.close(); + return success; + } + }); + } + + void toast(final String msg) { + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(LoginActivity.this, msg, Toast.LENGTH_LONG).show(); + } + }); + } + + @Override + public void onAccept(final String name, final String password) { File walletFolder = getStorageRoot(); - if (!walletFolder.isDirectory()) { - Timber.e("Wallet dir " + walletFolder.getAbsolutePath() + "is not a directory"); - return false; - } - File cacheFile = new File(walletFolder, walletName); - File keysFile = new File(walletFolder, walletName + ".keys"); - File addressFile = new File(walletFolder, walletName + ".address.txt"); + File walletFile = new File(walletFolder, name); + Timber.d("New Wallet %s", walletFile.getAbsolutePath()); + walletFile.delete(); // when recovering wallets, the cache seems corrupt - if (cacheFile.exists() || keysFile.exists() || addressFile.exists()) { - Timber.e("Some wallet files already exist for %s", cacheFile.getAbsolutePath()); - return false; - } + boolean rc = testWallet(walletFile.getAbsolutePath(), password) == Wallet.Status.Status_Ok; - newWalletFile = new File(walletFolder, walletName); - boolean success = walletCreator.createWallet(newWalletFile, walletPassword); - if (success) { - return true; + if (rc) { + popFragmentStack(GENERATE_STACK); + Toast.makeText(LoginActivity.this, + getString(R.string.generate_wallet_created), Toast.LENGTH_SHORT).show(); } else { - Timber.e("Could not create new wallet in %s", newWalletFile.getAbsolutePath()); - return false; + Timber.e("Wallet store failed to %s", walletFile.getAbsolutePath()); + Toast.makeText(LoginActivity.this, getString(R.string.generate_wallet_create_failed), Toast.LENGTH_LONG).show(); } } - @Override - protected void onPostExecute(Boolean result) { - super.onPostExecute(result); - if (isDestroyed()) { - return; - } - dismissProgressDialog(); - if (result) { - startDetails(newWalletFile, walletPassword, GenerateReviewFragment.VIEW_TYPE_ACCEPT); + Wallet.Status testWallet(String path, String password) { + Timber.d("testing wallet %s", 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(); + Timber.d("wallet tested %s", aWallet.getStatus()); + aWallet.close(); + return status; + } + + 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 { - walletGenerateError(); + return new File(dir, name).exists() + && new File(dir, name + ".keys").exists() + && new File(dir, name + ".address.txt").exists(); } } - } - - public void createWallet(final String name, final String password, - final WalletCreator walletCreator) { - new AsyncCreateWallet(name, password, walletCreator) - .executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR); - } - - void walletGenerateError() { - try { - GenerateFragment genFragment = (GenerateFragment) - getSupportFragmentManager().findFragmentById(R.id.fragment_container); - genFragment.walletGenerateError(); - } catch (ClassCastException ex) { - Timber.e("walletGenerateError() but not in GenerateFragment"); - } - } - - interface WalletCreator { - boolean createWallet(File aFile, String password); - - } - - @Override - public void onGenerate(final String name, final String password) { - createWallet(name, password, - new WalletCreator() { - public boolean createWallet(File aFile, String password) { - Wallet newWallet = WalletManager.getInstance() - .createWallet(aFile, password, MNEMONIC_LANGUAGE); - boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok); - if (!success) { - Timber.e(newWallet.getErrorString()); - toast(newWallet.getErrorString()); - } - newWallet.close(); - return success; - } - }); - } - - @Override - public void onGenerate(final String name, final String password, final String seed, - final long restoreHeight) { - createWallet(name, password, - new WalletCreator() { - public boolean createWallet(File aFile, String password) { - Wallet newWallet = WalletManager.getInstance(). - recoveryWallet(aFile, password, seed, restoreHeight); - boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok); - if (!success) { - Timber.e(newWallet.getErrorString()); - toast(newWallet.getErrorString()); - } - newWallet.close(); - return success; - } - }); - } - - @Override - public void onGenerate(final String name, final String password, - final String address, final String viewKey, final String spendKey, - final long restoreHeight) { - createWallet(name, password, - new WalletCreator() { - public boolean createWallet(File aFile, String password) { - Wallet newWallet = WalletManager.getInstance() - .createWalletWithKeys(aFile, password, MNEMONIC_LANGUAGE, restoreHeight, - address, viewKey, spendKey); - boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok); - if (!success) { - Timber.e(newWallet.getErrorString()); - toast(newWallet.getErrorString()); - } - newWallet.close(); - return success; - } - }); - } - void toast(final String msg) { - runOnUiThread(new Runnable() { - @Override - public void run() { - Toast.makeText(LoginActivity.this, msg, Toast.LENGTH_LONG).show(); - } - }); - } - - @Override - public void onAccept(final String name, final String password) { - File walletFolder = getStorageRoot(); - File walletFile = new File(walletFolder, name); - Timber.d("New Wallet %s", walletFile.getAbsolutePath()); - walletFile.delete(); // when recovering wallets, the cache seems corrupt - - boolean rc = 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 { - Timber.e("Wallet store failed to %s", walletFile.getAbsolutePath()); - Toast.makeText(LoginActivity.this, getString(R.string.generate_wallet_create_failed), Toast.LENGTH_LONG).show(); - } - } - - Wallet.Status testWallet(String path, String password) { - Timber.d("testing wallet %s", 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(); - Timber.d("wallet tested %s", aWallet.getStatus()); - aWallet.close(); - return status; - } - - 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, boolean ignoreCacheError) { - if (walletExists(dstWallet, true) && !overwrite) return false; - boolean success = false; - File srcDir = srcWallet.getParentFile(); - String srcName = srcWallet.getName(); - File dstDir = dstWallet.getParentFile(); - String dstName = dstWallet.getName(); - try { + boolean copyWallet(File srcWallet, File dstWallet, boolean overwrite, boolean ignoreCacheError) { + if (walletExists(dstWallet, true) && !overwrite) return false; + boolean success = false; + File srcDir = srcWallet.getParentFile(); + String srcName = srcWallet.getName(); + File dstDir = dstWallet.getParentFile(); + String dstName = dstWallet.getName(); try { - copyFile(new File(srcDir, srcName), new File(dstDir, dstName)); - } catch (IOException ex) { - Timber.d("CACHE %s", ignoreCacheError); - if (!ignoreCacheError) { // ignore cache backup error if backing up (can be resynced) - throw ex; + try { + copyFile(new File(srcDir, srcName), new File(dstDir, dstName)); + } catch (IOException ex) { + Timber.d("CACHE %s", ignoreCacheError); + if (!ignoreCacheError) { // ignore cache backup error if backing up (can be resynced) + throw ex; + } } + 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) { + Timber.e("wallet copy failed: %s", ex.getMessage()); + // try to rollback + deleteWallet(dstWallet); } - 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) { - Timber.e("wallet copy failed: %s", ex.getMessage()); - // try to rollback - deleteWallet(dstWallet); - } - return success; - } - - // do our best to delete as much as possible of the wallet files - boolean deleteWallet(File walletFile) { - Timber.d("deleteWallet %s", walletFile.getAbsolutePath()); - File dir = walletFile.getParentFile(); - String name = walletFile.getName(); - boolean success = true; - File cacheFile = new File(dir, name); - if (cacheFile.exists()) { - success = cacheFile.delete(); - } - success = new File(dir, name + ".keys").delete() && success; - File addressFile = new File(dir, name + ".address.txt"); - if (addressFile.exists()) { - success = addressFile.delete() && success; - } - Timber.d("deleteWallet is %s", 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 { - if (inChannel != null) - inChannel.close(); - if (outChannel != null) - outChannel.close(); - } - } - - @Override - public void onBackPressed() { - Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container); - if (f instanceof GenerateReviewFragment) { - if (((GenerateReviewFragment) f).backOk()) { - super.onBackPressed(); + return success; + } + + // do our best to delete as much as possible of the wallet files + boolean deleteWallet(File walletFile) { + Timber.d("deleteWallet %s", walletFile.getAbsolutePath()); + File dir = walletFile.getParentFile(); + String name = walletFile.getName(); + boolean success = true; + File cacheFile = new File(dir, name); + if (cacheFile.exists()) { + success = cacheFile.delete(); } - } else if (f instanceof LoginFragment) { - if (((LoginFragment) f).isFabOpen()) { - ((LoginFragment) f).animateFAB(); - } else { - super.onBackPressed(); + success = new File(dir, name + ".keys").delete() && success; + File addressFile = new File(dir, name + ".address.txt"); + if (addressFile.exists()) { + success = addressFile.delete() && success; } - } else { - super.onBackPressed(); + Timber.d("deleteWallet is %s", success); + return success; } - } - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_create_help_new: - HelpFragment.display(getSupportFragmentManager(), R.string.help_create_new); - return true; - case R.id.action_create_help_keys: - HelpFragment.display(getSupportFragmentManager(), R.string.help_create_keys); - return true; - case R.id.action_create_help_view: - HelpFragment.display(getSupportFragmentManager(), R.string.help_create_view); - return true; - case R.id.action_create_help_seed: - HelpFragment.display(getSupportFragmentManager(), R.string.help_create_seed); - return true; - case R.id.action_details_help: - HelpFragment.display(getSupportFragmentManager(), R.string.help_details); - return true; - case R.id.action_license_info: - AboutFragment.display(getSupportFragmentManager()); - return true; - case R.id.action_help_list: - HelpFragment.display(getSupportFragmentManager(), R.string.help_list); - return true; - case R.id.action_privacy_policy: - PrivacyFragment.display(getSupportFragmentManager()); - return true; - case R.id.action_testnet: - try { - LoginFragment loginFragment = (LoginFragment) - getSupportFragmentManager().findFragmentById(R.id.fragment_container); - item.setChecked(loginFragment.onTestnetMenuItem()); - } catch (ClassCastException ex) { - } - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - private void setNet(boolean testnet) { - WalletManager.getInstance().setDaemon("", testnet, "", ""); - } - - static class WalletNode { - String name = null; - String host = ""; - int port = 28081; - String user = ""; - String password = ""; - boolean isTestnet; - - WalletNode(String walletName, String daemon, boolean isTestnet) { - if ((daemon == null) || daemon.isEmpty()) return; - this.name = walletName; - String daemonAddress; - String a[] = daemon.split("@"); - if (a.length == 1) { // no credentials - daemonAddress = a[0]; - } else if (a.length == 2) { // credentials - String userPassword[] = a[0].split(":"); - if (userPassword.length != 2) - throw new IllegalArgumentException("User:Password invalid"); - user = userPassword[0]; - if (!user.isEmpty()) password = userPassword[1]; - daemonAddress = a[1]; - } else { - throw new IllegalArgumentException("Too many @"); + 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 { + if (inChannel != null) + inChannel.close(); + if (outChannel != null) + outChannel.close(); } + } - String da[] = daemonAddress.split(":"); - if ((da.length > 2) || (da.length < 1)) - throw new IllegalArgumentException("Too many ':' or too few"); - host = da[0]; - if (da.length == 2) { - try { - port = Integer.parseInt(da[1]); - } catch (NumberFormatException ex) { - throw new IllegalArgumentException("Port not numeric"); + @Override + public void onBackPressed() { + Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container); + if (f instanceof GenerateReviewFragment) { + if (((GenerateReviewFragment) f).backOk()) { + super.onBackPressed(); + } + } else if (f instanceof LoginFragment) { + if (((LoginFragment) f).isFabOpen()) { + ((LoginFragment) f).animateFAB(); + } else { + super.onBackPressed(); } } else { - port = (isTestnet ? 28081 : 18081); + super.onBackPressed(); } - this.isTestnet = isTestnet; } - String getAddress() { - return host + ":" + port; + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_create_help_new: + HelpFragment.display(getSupportFragmentManager(), R.string.help_create_new); + return true; + case R.id.action_create_help_keys: + HelpFragment.display(getSupportFragmentManager(), R.string.help_create_keys); + return true; + case R.id.action_create_help_view: + HelpFragment.display(getSupportFragmentManager(), R.string.help_create_view); + return true; + case R.id.action_create_help_seed: + HelpFragment.display(getSupportFragmentManager(), R.string.help_create_seed); + return true; + case R.id.action_details_help: + HelpFragment.display(getSupportFragmentManager(), R.string.help_details); + return true; + case R.id.action_license_info: + AboutFragment.display(getSupportFragmentManager()); + return true; + case R.id.action_help_list: + HelpFragment.display(getSupportFragmentManager(), R.string.help_list); + return true; + case R.id.action_privacy_policy: + PrivacyFragment.display(getSupportFragmentManager()); + return true; + case R.id.action_testnet: + try { + LoginFragment loginFragment = (LoginFragment) + getSupportFragmentManager().findFragmentById(R.id.fragment_container); + item.setChecked(loginFragment.onTestnetMenuItem()); + } catch (ClassCastException ex) { + } + return true; + default: + return super.onOptionsItemSelected(item); + } } - boolean isValid() { - return !host.isEmpty(); + private void setNet(boolean testnet) { + WalletManager.getInstance().setDaemon("", testnet, "", ""); } - } - private class AsyncOpenWallet extends AsyncTask { - final static int OK = 0; - final static int TIMEOUT = 1; - final static int INVALID = 2; - final static int IOEX = 3; + static class WalletNode { + String name = null; + String host = ""; + int port = 28081; + String user = ""; + String password = ""; + boolean isTestnet; + + WalletNode(String walletName, String daemon, boolean isTestnet) { + if ((daemon == null) || daemon.isEmpty()) return; + this.name = walletName; + String daemonAddress; + String a[] = daemon.split("@"); + if (a.length == 1) { // no credentials + daemonAddress = a[0]; + } else if (a.length == 2) { // credentials + String userPassword[] = a[0].split(":"); + if (userPassword.length != 2) + throw new IllegalArgumentException("User:Password invalid"); + user = userPassword[0]; + if (!user.isEmpty()) password = userPassword[1]; + daemonAddress = a[1]; + } else { + throw new IllegalArgumentException("Too many @"); + } - WalletNode walletNode; + String da[] = daemonAddress.split(":"); + if ((da.length > 2) || (da.length < 1)) + throw new IllegalArgumentException("Too many ':' or too few"); + host = da[0]; + if (da.length == 2) { + try { + port = Integer.parseInt(da[1]); + } catch (NumberFormatException ex) { + throw new IllegalArgumentException("Port not numeric"); + } + } else { + port = (isTestnet ? 28081 : 18081); + } + this.isTestnet = isTestnet; + } - @Override - protected void onPreExecute() { - super.onPreExecute(); - showProgressDialog(R.string.open_progress, DAEMON_TIMEOUT / 4); + String getAddress() { + return host + ":" + port; + } + + boolean isValid() { + return !host.isEmpty(); + } } - @Override - protected Integer doInBackground(WalletNode... params) { - if (params.length != 1) return INVALID; - this.walletNode = params[0]; - if (!walletNode.isValid()) return INVALID; + private class AsyncOpenWallet extends AsyncTask { + final static int OK = 0; + final static int TIMEOUT = 1; + final static int INVALID = 2; + final static int IOEX = 3; - Timber.d("checking %s", walletNode.getAddress()); + WalletNode walletNode; - try { - long timeDA = new Date().getTime(); - SocketAddress address = new InetSocketAddress(walletNode.host, walletNode.port); - long timeDB = new Date().getTime(); - Timber.d("Resolving " + walletNode.host + " took " + (timeDB - timeDA) + "ms."); - Socket socket = new Socket(); - long timeA = new Date().getTime(); - socket.connect(address, LoginActivity.DAEMON_TIMEOUT); - socket.close(); - long timeB = new Date().getTime(); - long time = timeB - timeA; - Timber.d("Daemon " + walletNode.host + " is " + time + "ms away."); - return (time < LoginActivity.DAEMON_TIMEOUT ? OK : TIMEOUT); - } catch (IOException ex) { - Timber.d("Cannot reach daemon " + walletNode.host + "/" + walletNode.port + " because " + ex.getMessage()); - return IOEX; - } catch (IllegalArgumentException ex) { - Timber.d("Cannot reach daemon " + walletNode.host + "/" + walletNode.port + " because " + ex.getMessage()); - return INVALID; + @Override + protected void onPreExecute() { + super.onPreExecute(); + showProgressDialog(R.string.open_progress, DAEMON_TIMEOUT / 4); } - } - @Override - protected void onPostExecute(Integer result) { - super.onPostExecute(result); - if (isDestroyed()) { - return; + @Override + protected Integer doInBackground(WalletNode... params) { + if (params.length != 1) return INVALID; + this.walletNode = params[0]; + if (!walletNode.isValid()) return INVALID; + + Timber.d("checking %s", walletNode.getAddress()); + + try { + long timeDA = new Date().getTime(); + SocketAddress address = new InetSocketAddress(walletNode.host, walletNode.port); + long timeDB = new Date().getTime(); + Timber.d("Resolving " + walletNode.host + " took " + (timeDB - timeDA) + "ms."); + Socket socket = new Socket(); + long timeA = new Date().getTime(); + socket.connect(address, LoginActivity.DAEMON_TIMEOUT); + socket.close(); + long timeB = new Date().getTime(); + long time = timeB - timeA; + Timber.d("Daemon " + walletNode.host + " is " + time + "ms away."); + return (time < LoginActivity.DAEMON_TIMEOUT ? OK : TIMEOUT); + } catch (IOException ex) { + Timber.d("Cannot reach daemon " + walletNode.host + "/" + walletNode.port + " because " + ex.getMessage()); + return IOEX; + } catch (IllegalArgumentException ex) { + Timber.d("Cannot reach daemon " + walletNode.host + "/" + walletNode.port + " because " + ex.getMessage()); + return INVALID; + } } - dismissProgressDialog(); - switch (result) { - case OK: - Timber.d("selected wallet is ." + walletNode.name + "."); - // now it's getting real, onValidateFields if wallet exists - promptAndStart(walletNode); - break; - case TIMEOUT: - Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_connect_timeout), Toast.LENGTH_LONG).show(); - break; - case INVALID: - Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_node_invalid), Toast.LENGTH_LONG).show(); - break; - case IOEX: - Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_connect_ioex), Toast.LENGTH_LONG).show(); - break; + + @Override + protected void onPostExecute(Integer result) { + super.onPostExecute(result); + if (isDestroyed()) { + return; + } + dismissProgressDialog(); + switch (result) { + case OK: + Timber.d("selected wallet is ." + walletNode.name + "."); + // now it's getting real, onValidateFields if wallet exists + promptAndStart(walletNode); + break; + case TIMEOUT: + Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_connect_timeout), Toast.LENGTH_LONG).show(); + break; + case INVALID: + Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_node_invalid), Toast.LENGTH_LONG).show(); + break; + case IOEX: + Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_connect_ioex), Toast.LENGTH_LONG).show(); + break; + } } } - } - void promptAndStart(WalletNode walletNode) { - File walletFile = Helper.getWalletFile(this, walletNode.name); - if (WalletManager.getInstance().walletExists(walletFile)) { - WalletManager.getInstance(). - setDaemon(walletNode.getAddress(), walletNode.isTestnet, walletNode.user, walletNode.password); - promptPassword(walletNode.name, new PasswordAction() { - @Override - public void action(String walletName, String password) { - startWallet(walletName, password); - } - }); - } else { // this cannot really happen as we prefilter choices - Toast.makeText(this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show(); + void promptAndStart(WalletNode walletNode) { + File walletFile = Helper.getWalletFile(this, walletNode.name); + if (WalletManager.getInstance().walletExists(walletFile)) { + WalletManager.getInstance(). + setDaemon(walletNode.getAddress(), walletNode.isTestnet, walletNode.user, walletNode.password); + promptPassword(walletNode.name, new PasswordAction() { + @Override + public void action(String walletName, String password) { + startWallet(walletName, password); + } + }); + } else { // this cannot really happen as we prefilter choices + Toast.makeText(this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show(); + } } - } } diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java index df480f8dbd..62d321eb70 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java @@ -89,7 +89,9 @@ public interface Listener { void onWalletRename(String name); - void onWalletBackup(String name); + void onWalletBackupToFile(String name); + + void onWalletBackupToNFC(String name); void onWalletArchive(String walletName); @@ -267,8 +269,14 @@ public boolean onContextInteraction(MenuItem item, WalletManager.WalletInfo list case R.id.action_rename: activityCallback.onWalletRename(listItem.name); break; - case R.id.action_backup: + /*case R.id.action_backup: activityCallback.onWalletBackup(listItem.name); + break;*/ + case R.id.action_backup_file: + activityCallback.onWalletBackupToFile(listItem.name); + break; + case R.id.action_backup_nfc: //TODO 备份到NFC + activityCallback.onWalletBackupToNFC(listItem.name); break; case R.id.action_archive: activityCallback.onWalletArchive(listItem.name); diff --git a/app/src/main/res/menu/list_context_menu.xml b/app/src/main/res/menu/list_context_menu.xml index 833369872b..dbdcf7cf88 100644 --- a/app/src/main/res/menu/list_context_menu.xml +++ b/app/src/main/res/menu/list_context_menu.xml @@ -16,8 +16,12 @@ android:title="@string/menu_rename" /> + android:id="@+id/action_backup_file" + android:title="@string/menu_backup_to_file" /> + + ¡Cambio de nombre fallido! Nodo - ([<usuario>:<contraseña>@]<daemon>[:<puerto>]) + ([<user>:<pass>@]<daemon>[:<port>]) Selección de Red TestNet MainNet @@ -337,4 +337,6 @@ Soy monerujo Orden XMR.TO Pago en BTC activado, toca para más info. + Backup To File + Backup To NFC diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6bf7e7587f..bd59b93a2f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -14,6 +14,7 @@ Rename … Archive Backup + Backup To NFC Continue typing … Meh … @@ -371,4 +372,5 @@ Restore view-only wallet Restore wallet from private keys Restore wallet 25 word seed + Backup To File From 6be8bbdcbecbd1c5a3dc0a44fbf77c2b616198bc Mon Sep 17 00:00:00 2001 From: "jianjunchu@gmail.com" Date: Fri, 20 Apr 2018 21:05:32 +0800 Subject: [PATCH 02/12] a --- .idea/codeStyles/Project.xml | 29 +++++++++++++++++++++++++++++ .idea/misc.xml | 9 +++++++++ 2 files changed, 38 insertions(+) create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/misc.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000000..30aa626c23 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000000..7bfef59df1 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file From 49acbb11fd4ddcb6f6518a55e689b3af6c4023cb Mon Sep 17 00:00:00 2001 From: "jianjunchu@gmail.com" Date: Wed, 2 May 2018 19:46:10 +0800 Subject: [PATCH 03/12] delete --- .../com/m2049r/xmrwallet/LoginActivity.java | 1690 ----------------- app/src/main/res/values-es/strings.xml | 350 ---- 2 files changed, 2040 deletions(-) delete mode 100644 app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java delete mode 100644 app/src/main/res/values-es/strings.xml diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java deleted file mode 100644 index 701d665353..0000000000 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java +++ /dev/null @@ -1,1690 +0,0 @@ -/* - * 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.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.media.MediaScannerConnection; -import android.os.AsyncTask; -import android.os.Bundle; -import android.os.Handler; -import android.support.annotation.NonNull; -import android.support.design.widget.TextInputLayout; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; -import android.text.Editable; -import android.text.TextWatcher; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.inputmethod.EditorInfo; -import android.widget.Button; -import android.widget.EditText; -import android.widget.TextView; -import android.widget.Toast; - -import com.m2049r.xmrwallet.data.WalletNode; -import com.m2049r.xmrwallet.dialog.AboutFragment; -import com.m2049r.xmrwallet.dialog.CreditsFragment; -import com.m2049r.xmrwallet.dialog.HelpFragment; -import com.m2049r.xmrwallet.dialog.PrivacyFragment; -import com.m2049r.xmrwallet.model.NetworkType; -import com.m2049r.xmrwallet.model.Wallet; -import com.m2049r.xmrwallet.model.WalletManager; -import com.m2049r.xmrwallet.service.WalletService; -import com.m2049r.xmrwallet.util.CrazyPassEncoder; -import com.m2049r.xmrwallet.util.Helper; -import com.m2049r.xmrwallet.util.KeyStoreHelper; -import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor; -import com.m2049r.xmrwallet.widget.Toolbar; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.Socket; -import java.net.SocketAddress; -import java.nio.channels.FileChannel; -import java.util.Date; - -import timber.log.Timber; - -public class LoginActivity extends SecureActivity - implements LoginFragment.Listener, GenerateFragment.Listener, -<<<<<<< HEAD - GenerateReviewFragment.Listener, GenerateReviewFragment.AcceptListener, - GenerateReviewFragment.ProgressListener, ReceiveFragment.Listener { - private static final String GENERATE_STACK = "gen"; -======= - GenerateReviewFragment.Listener, GenerateReviewFragment.AcceptListener, ReceiveFragment.Listener { - private static final String GENERATE_STACK = "gen"; ->>>>>>> 6ce4b42f2effa3ec3525b3849c8f20ada88267d5 - - static final int DAEMON_TIMEOUT = 500; // deamon must respond in 500ms - - private Toolbar toolbar; - - @Override - public void setToolbarButton(int type) { - toolbar.setButton(type); - } - - @Override - public void setTitle(String title) { - toolbar.setTitle(title); - } - - @Override - public void setSubtitle(String subtitle) { - toolbar.setSubtitle(subtitle); - } - - @Override - public void setTitle(String title, String subtitle) { - toolbar.setTitle(title, subtitle); - } - -<<<<<<< HEAD - toolbar.setOnButtonListener(new Toolbar.OnButtonListener() { - @Override - public void onButton(int type) { - switch (type) { - case Toolbar.BUTTON_BACK: - onBackPressed(); - break; - case Toolbar.BUTTON_CLOSE: - finish(); - break; - case Toolbar.BUTTON_CREDITS: - CreditsFragment.display(getSupportFragmentManager()); - break; - case Toolbar.BUTTON_NONE: - default: - Timber.e("Button " + type + "pressed - how can this be?"); - } -======= - @Override - protected void onCreate(Bundle savedInstanceState) { - Timber.d("onCreate()"); - super.onCreate(savedInstanceState); - if (savedInstanceState != null) { - // we don't store anything ourselves ->>>>>>> 6ce4b42f2effa3ec3525b3849c8f20ada88267d5 - } - - setContentView(R.layout.activity_login); - toolbar = (Toolbar) findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - getSupportActionBar().setDisplayShowTitleEnabled(false); - -<<<<<<< HEAD - boolean checkServiceRunning() { - if (WalletService.Running) { - Toast.makeText(this, getString(R.string.service_busy), Toast.LENGTH_SHORT).show(); - return true; - } else { - return false; - } - } - - @Override - public boolean onWalletSelected(String walletName, String daemon) { - if (daemon.length() == 0) { - Toast.makeText(this, getString(R.string.prompt_daemon_missing), Toast.LENGTH_SHORT).show(); - return false; - } - if (checkServiceRunning()) return false; - try { - WalletNode aWalletNode = new WalletNode(walletName, daemon, WalletManager.getInstance().getNetworkType()); - new AsyncOpenWallet().execute(aWalletNode); - } catch (IllegalArgumentException ex) { - Timber.e(ex.getLocalizedMessage()); - Toast.makeText(this, ex.getLocalizedMessage(), Toast.LENGTH_SHORT).show(); - return false; - } - return true; - } - - @Override - public void onWalletDetails(final String walletName) { - Timber.d("details for wallet .%s.", walletName); - if (checkServiceRunning()) return; - DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - switch (which) { - case DialogInterface.BUTTON_POSITIVE: - final File walletFile = Helper.getWalletFile(LoginActivity.this, walletName); - if (WalletManager.getInstance().walletExists(walletFile)) { - promptPassword(walletName, new PasswordAction() { - @Override - public void action(String walletName, String password) { - startDetails(walletFile, password, GenerateReviewFragment.VIEW_TYPE_DETAILS); - } - }); - } else { // this cannot really happen as we prefilter choices - Timber.e("Wallet missing: %s", walletName); - Toast.makeText(LoginActivity.this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show(); - } - break; - - case DialogInterface.BUTTON_NEGATIVE: - // do nothing - break; - } - } - }; - - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setMessage(getString(R.string.details_alert_message)) - .setPositiveButton(getString(R.string.details_alert_yes), dialogClickListener) - .setNegativeButton(getString(R.string.details_alert_no), dialogClickListener) - .show(); - } - - @Override - public void onWalletReceive(String walletName) { - Timber.d("receive for wallet .%s.", walletName); - if (checkServiceRunning()) return; - final File walletFile = Helper.getWalletFile(this, walletName); - if (WalletManager.getInstance().walletExists(walletFile)) { - promptPassword(walletName, new PasswordAction() { -======= - toolbar.setOnButtonListener(new Toolbar.OnButtonListener() { ->>>>>>> 6ce4b42f2effa3ec3525b3849c8f20ada88267d5 - @Override - public void onButton(int type) { - switch (type) { - case Toolbar.BUTTON_BACK: - onBackPressed(); - break; - case Toolbar.BUTTON_CLOSE: - finish(); - break; - case Toolbar.BUTTON_DONATE: - DonationFragment.display(getSupportFragmentManager()); - break; - case Toolbar.BUTTON_NONE: - default: - Timber.e("Button " + type + "pressed - how can this be?"); - } - } - }); - - if (Helper.getWritePermission(this)) { - if (savedInstanceState == null) startLoginFragment(); - } else { - Timber.i("Waiting for permissions"); - } - } - - boolean checkServiceRunning() { - if (WalletService.Running) { - Toast.makeText(this, getString(R.string.service_busy), Toast.LENGTH_SHORT).show(); - return true; - } else { - return false; - } - } - - @Override - public boolean onWalletSelected(String walletName, String daemon, boolean testnet) { - if (daemon.length() == 0) { - Toast.makeText(this, getString(R.string.prompt_daemon_missing), Toast.LENGTH_SHORT).show(); - return false; - } - if (checkServiceRunning()) return false; - try { - WalletNode aWalletNode = new WalletNode(walletName, daemon, testnet); - new AsyncOpenWallet().execute(aWalletNode); - } catch (IllegalArgumentException ex) { - Timber.e(ex.getLocalizedMessage()); - Toast.makeText(this, ex.getLocalizedMessage(), Toast.LENGTH_SHORT).show(); - return false; - } - return true; - } - - @Override - public void onWalletDetails(final String walletName, boolean testnet) { - setNet(testnet); - Timber.d("details 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: - final File walletFile = Helper.getWalletFile(LoginActivity.this, walletName); - if (WalletManager.getInstance().walletExists(walletFile)) { - promptPassword(walletName, new PasswordAction() { - @Override - public void action(String walletName, String password) { - startDetails(walletFile, password, GenerateReviewFragment.VIEW_TYPE_DETAILS); - } - }); - } else { // this cannot really happen as we prefilter choices - Timber.e("Wallet missing: %s", walletName); - Toast.makeText(LoginActivity.this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show(); - } - break; - - case DialogInterface.BUTTON_NEGATIVE: - // do nothing - break; - } - } - }; - - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setMessage(getString(R.string.details_alert_message)) - .setPositiveButton(getString(R.string.details_alert_yes), dialogClickListener) - .setNegativeButton(getString(R.string.details_alert_no), dialogClickListener) - .show(); - } - - @Override - public void onWalletReceive(String walletName, boolean testnet) { - setNet(testnet); - Timber.d("receive for wallet ." + walletName + "."); - if (checkServiceRunning()) return; - final File walletFile = Helper.getWalletFile(this, walletName); - if (WalletManager.getInstance().walletExists(walletFile)) { - promptPassword(walletName, new PasswordAction() { - @Override - public void action(String walletName, String password) { - startReceive(walletFile, password); - } - }); - } else { // this cannot really happen as we prefilter choices - Toast.makeText(this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show(); - } - } - - private class AsyncRename extends AsyncTask { - @Override - protected void onPreExecute() { - super.onPreExecute(); - showProgressDialog(R.string.rename_progress); - } - - @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); - if (isDestroyed()) { - return; - } - dismissProgressDialog(); - if (result) { - reloadWalletList(); - } else { - Toast.makeText(LoginActivity.this, getString(R.string.rename_failed), Toast.LENGTH_LONG).show(); - } - } - } - - // copy + delete seems safer than rename because we call rollback easily - boolean renameWallet(File walletFile, String newName) { - if (copyWallet(walletFile, new File(walletFile.getParentFile(), newName), false, true)) { - deleteWallet(walletFile); - return true; - } else { - return false; - } - } - - @Override - public void onWalletRename(final String walletName) { - Timber.d("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(getString(R.string.label_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(getString(R.string.label_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 AsyncBackupToFile extends AsyncTask { - @Override - protected void onPreExecute() { - super.onPreExecute(); - showProgressDialog(R.string.backup_progress); - } - - @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); - if (isDestroyed()) { - return; - } - dismissProgressDialog(); - if (!result) { - Toast.makeText(LoginActivity.this, getString(R.string.backup_failed), Toast.LENGTH_LONG).show(); - } - } - } - -<<<<<<< HEAD - public void onWalletChangePassword() {//final String walletName, final String walletPassword) { - try { - GenerateReviewFragment detailsFragment = (GenerateReviewFragment) - getSupportFragmentManager().findFragmentById(R.id.fragment_container); - AlertDialog dialog = detailsFragment.createChangePasswordDialog(); - if (dialog != null) { - Helper.showKeyboard(dialog); - dialog.show(); - } - } catch (ClassCastException ex) { - Timber.w("onWalletChangePassword() called, but no GenerateReviewFragment active"); - } - } - - @Override - public void onAddWallet(String type) { - if (checkServiceRunning()) return; - startGenerateFragment(type); - } - - AlertDialog openDialog = null; // for preventing opening of multiple dialogs - - void promptPassword(final String wallet, final PasswordAction action) { - if (openDialog != null) return; // we are already asking for password - Context context = LoginActivity.this; - LayoutInflater li = LayoutInflater.from(context); - View promptsView = li.inflate(R.layout.prompt_password, null); -======= - private boolean backupWallet(String walletName) { - File backupFolder = new File(getStorageRoot(), "backups"); - if (!backupFolder.exists()) { - if (!backupFolder.mkdir()) { - Timber.e("Cannot create backup dir %s", backupFolder.getAbsolutePath()); - return false; - } - // make folder visible over USB/MTP - MediaScannerConnection.scanFile(this, new String[]{backupFolder.toString()}, null, null); - } - File walletFile = Helper.getWalletFile(LoginActivity.this, walletName); - File backupFile = new File(backupFolder, walletName); - Timber.d("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 - boolean success = copyWallet(walletFile, backupFile, true, true); - Timber.d("copyWallet is %s", success); - return success; - } - - /* - modified into onWalletBackupToFile - @Override - public void onWalletBackup(String walletName) { - Timber.d("backup for wallet ." + walletName + "."); - new AsyncBackup().execute(walletName); - }*/ - - @Override - public void onWalletBackupToFile(String walletName) { - Timber.d("backup to file for wallet ." + walletName + "."); - new AsyncBackupToFile().execute(walletName); - } ->>>>>>> 6ce4b42f2effa3ec3525b3849c8f20ada88267d5 - - @Override - public void onWalletBackupToNFC(String walletName) { - Timber.d("backup to nfc for wallet ." + walletName + "."); - - } - - private class AsyncArchive extends AsyncTask { - @Override - protected void onPreExecute() { - super.onPreExecute(); - showProgressDialog(R.string.archive_progress); - } - - @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); - if (isDestroyed()) { - return; - } - dismissProgressDialog(); - if (result) { - reloadWalletList(); - } else { - Toast.makeText(LoginActivity.this, getString(R.string.archive_failed), Toast.LENGTH_LONG).show(); - } - } - } - - @Override - public void onWalletArchive(final String walletName) { - Timber.d("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() { - Timber.d("reloadWalletList()"); - try { - LoginFragment loginFragment = (LoginFragment) - getSupportFragmentManager().findFragmentById(R.id.fragment_container); - if (loginFragment != null) { - loginFragment.loadList(); - } - } catch (ClassCastException ex) { - } - } - - @Override - public void onAddWallet(boolean testnet, String type) { - setNet(testnet); - if (checkServiceRunning()) return; - startGenerateFragment(type); - } - - AlertDialog passwordDialog = null; // for preventing multiple clicks in wallet list - - void promptPassword(final String wallet, final PasswordAction action) { - if (passwordDialog != null) return; // we are already asking for password - Context context = LoginActivity.this; - LayoutInflater li = LayoutInflater.from(context); - View promptsView = li.inflate(R.layout.prompt_password, null); - - AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context); - alertDialogBuilder.setView(promptsView); - - final TextInputLayout etPassword = (TextInputLayout) promptsView.findViewById(R.id.etPassword); - etPassword.setHint(LoginActivity.this.getString(R.string.prompt_password, wallet)); - - etPassword.getEditText().addTextChangedListener(new TextWatcher() { - - @Override - public void afterTextChanged(Editable s) { - if (etPassword.getError() != null) { - etPassword.setError(null); - } - } - - @Override - public void beforeTextChanged(CharSequence s, int start, - int count, int after) { - } - - @Override - public void onTextChanged(CharSequence s, int start, - int before, int count) { - } - }); - - // set dialog message - alertDialogBuilder - .setCancelable(false) - .setPositiveButton(getString(R.string.label_ok), null) - .setNegativeButton(getString(R.string.label_cancel), - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - Helper.hideKeyboardAlways(LoginActivity.this); - dialog.cancel(); - passwordDialog = null; - } - }); - passwordDialog = alertDialogBuilder.create(); - - passwordDialog.setOnShowListener(new DialogInterface.OnShowListener() { - @Override - public void onShow(DialogInterface dialog) { - Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE); - button.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - String pass = etPassword.getEditText().getText().toString(); - if (processPasswordEntry(wallet, pass, action)) { - Helper.hideKeyboardAlways(LoginActivity.this); -<<<<<<< HEAD - dialog.cancel(); - openDialog = null; - } - }); - openDialog = alertDialogBuilder.create(); - - openDialog.setOnShowListener(new DialogInterface.OnShowListener() { - @Override - public void onShow(DialogInterface dialog) { - Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE); - button.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { -======= - passwordDialog.dismiss(); - passwordDialog = null; - } else { - etPassword.setError(getString(R.string.bad_password)); - } - } - }); - } - }); - - Helper.showKeyboard(passwordDialog); - - // accept keyboard "ok" - etPassword.getEditText().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)) { ->>>>>>> 6ce4b42f2effa3ec3525b3849c8f20ada88267d5 - String pass = etPassword.getEditText().getText().toString(); - if (processPasswordEntry(wallet, pass, action)) { - Helper.hideKeyboardAlways(LoginActivity.this); - openDialog.dismiss(); - openDialog = null; - } else { - etPassword.setError(getString(R.string.bad_password)); - } - return true; - } - return false; - } - }); - -<<<<<<< HEAD - // accept keyboard "ok" - etPassword.getEditText().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)) { - String pass = etPassword.getEditText().getText().toString(); - if (processPasswordEntry(wallet, pass, action)) { - Helper.hideKeyboardAlways(LoginActivity.this); - openDialog.dismiss(); - openDialog = null; - } else { - etPassword.setError(getString(R.string.bad_password)); - } - return true; - } -======= - passwordDialog.show(); - } - - private boolean checkWalletPassword(String walletName, String password) { - String walletPath = new File(Helper.getStorageRoot(getApplicationContext()), - walletName + ".keys").getAbsolutePath(); - // only test view key - return WalletManager.getInstance().verifyWalletPassword(walletPath, password, true); - } - - interface PasswordAction { - void action(String walletName, String password); - } - - private boolean processPasswordEntry(String walletName, String pass, PasswordAction action) { - if (checkWalletPassword(walletName, pass)) { - action.action(walletName, pass); - return true; - } else { ->>>>>>> 6ce4b42f2effa3ec3525b3849c8f20ada88267d5 - return false; - } - } - -<<<<<<< HEAD - Helper.showKeyboard(openDialog); - openDialog.show(); - } -======= - //////////////////////////////////////// - // LoginFragment.Listener - //////////////////////////////////////// - @Override - public SharedPreferences getPrefs() { - return getPreferences(Context.MODE_PRIVATE); - } - - @Override - public File getStorageRoot() { - return Helper.getStorageRoot(getApplicationContext()); - } ->>>>>>> 6ce4b42f2effa3ec3525b3849c8f20ada88267d5 - - //////////////////////////////////////// - //////////////////////////////////////// - -<<<<<<< HEAD - private boolean processPasswordEntry(String walletName, String pass, PasswordAction action) { - String walletPassword = Helper.getWalletPassword(getApplicationContext(), walletName, pass); - if (walletPassword != null) { - action.action(walletName, walletPassword); - return true; - } else { - return false; - } - } - - //////////////////////////////////////// - // LoginFragment.Listener - //////////////////////////////////////// - @Override - public SharedPreferences getPrefs() { - return getPreferences(Context.MODE_PRIVATE); - } - - @Override - public File getStorageRoot() { - return Helper.getWalletRoot(getApplicationContext()); - } - - //////////////////////////////////////// - //////////////////////////////////////// - - @Override - public void showNet() { - switch (WalletManager.getInstance().getNetworkType()) { - case NetworkType_Mainnet: - toolbar.setSubtitle(getString(R.string.connect_mainnet)); - toolbar.setBackgroundResource(R.drawable.backgound_toolbar_mainnet); - break; - case NetworkType_Testnet: - toolbar.setSubtitle(getString(R.string.connect_testnet)); - toolbar.setBackgroundResource(R.color.colorPrimaryDark); - break; - case NetworkType_Stagenet: - toolbar.setSubtitle(getString(R.string.connect_stagenet)); - toolbar.setBackgroundResource(R.color.colorPrimaryDark); - break; - default: - throw new IllegalStateException("NetworkType unknown: " + WalletManager.getInstance().getNetworkType()); - } - } - - @Override - protected void onPause() { - Timber.d("onPause()"); - super.onPause(); - } - - ProgressDialog progressDialog = null; - - @Override - public void showProgressDialog(int msgId) { - showProgressDialog(msgId, 0); - } - - private void showProgressDialog(int msgId, long delay) { - dismissProgressDialog(); // just in case - progressDialog = new MyProgressDialog(LoginActivity.this, msgId); - if (delay > 0) { - Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - public void run() { - if (progressDialog != null) progressDialog.show(); - } - }, delay); - } else { - progressDialog.show(); -======= - @Override - public void showNet(boolean testnet) { - if (testnet) { - toolbar.setBackgroundResource(R.color.colorPrimaryDark); - } else { - toolbar.setBackgroundResource(R.drawable.backgound_toolbar_mainnet); - } - toolbar.setSubtitle(getString(testnet ? R.string.connect_testnet : R.string.connect_mainnet)); ->>>>>>> 6ce4b42f2effa3ec3525b3849c8f20ada88267d5 - } - -<<<<<<< HEAD - @Override - public void dismissProgressDialog() { - if (progressDialog != null && progressDialog.isShowing()) { - progressDialog.dismiss(); -======= - @Override - protected void onPause() { - Timber.d("onPause()"); - super.onPause(); ->>>>>>> 6ce4b42f2effa3ec3525b3849c8f20ada88267d5 - } - - ProgressDialog progressDialog = null; - - private void showProgressDialog(int msgId) { - showProgressDialog(msgId, 0); - } - - private void showProgressDialog(int msgId, long delay) { - dismissProgressDialog(); // just in case - progressDialog = new MyProgressDialog(LoginActivity.this, msgId); - if (delay > 0) { - Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - public void run() { - if (progressDialog != null) progressDialog.show(); - } - }, delay); - } else { - progressDialog.show(); - } - } - -<<<<<<< HEAD - MyProgressDialog(Activity activity, int msgId) { - super(activity); - this.activity = activity; - setCancelable(false); - setMessage(activity.getString(msgId)); -======= - private void dismissProgressDialog() { - if (progressDialog != null && progressDialog.isShowing()) { - progressDialog.dismiss(); - } - progressDialog = null; ->>>>>>> 6ce4b42f2effa3ec3525b3849c8f20ada88267d5 - } - - @Override - protected void onDestroy() { - dismissProgressDialog(); - super.onDestroy(); - } - - @Override - protected void onResume() { - super.onResume(); - Timber.d("onResume()"); - // wait for WalletService to finish - if (WalletService.Running && (progressDialog == null)) { - // and show a progress dialog, but only if there isn't one already - 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() { - // prevent back button - } - } - - - private class AsyncWaitForService extends AsyncTask { - @Override - protected void onPreExecute() { - super.onPreExecute(); - showProgressDialog(R.string.service_progress); - } - - @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); - if (isDestroyed()) { - return; - } - dismissProgressDialog(); - } - } - - void startWallet(String walletName, String walletPassword) { - Timber.d("startWallet()"); - Intent intent = new Intent(getApplicationContext(), WalletActivity.class); - intent.putExtra(WalletActivity.REQUEST_ID, walletName); - intent.putExtra(WalletActivity.REQUEST_PW, walletPassword); - startActivity(intent); - } - - void startDetails(File walletFile, String password, String type) { - Timber.d("startDetails()"); - Bundle b = new Bundle(); - b.putString("path", walletFile.getAbsolutePath()); - b.putString("password", password); - b.putString("type", type); - startReviewFragment(b); - } - - void startReceive(File walletFile, String password) { - Timber.d("startReceive()"); - Bundle b = new Bundle(); - b.putString("path", walletFile.getAbsolutePath()); - b.putString("password", password); - startReceiveFragment(b); - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { - Timber.d("onRequestPermissionsResult()"); - switch (requestCode) { - case Helper.PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE: - // If request is cancelled, the result arrays are empty. - if (grantResults.length > 0 - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - startLoginFragment = true; - } else { - String msg = getString(R.string.message_strorage_not_permitted); - Timber.e(msg); - Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); - } - break; - default: - } - } - - private boolean startLoginFragment = false; - - @Override - protected void onResumeFragments() { - super.onResumeFragments(); - if (startLoginFragment) { - startLoginFragment(); - startLoginFragment = false; - } - } - - void startLoginFragment() { - Fragment fragment = new LoginFragment(); - getSupportFragmentManager().beginTransaction() - .add(R.id.fragment_container, fragment).commit(); - Timber.d("LoginFragment added"); - } - - void startGenerateFragment(String type) { - Bundle extras = new Bundle(); - extras.putString(GenerateFragment.TYPE, type); - replaceFragment(new GenerateFragment(), GENERATE_STACK, extras); - Timber.d("GenerateFragment placed"); - } - - void startReviewFragment(Bundle extras) { - replaceFragment(new GenerateReviewFragment(), null, extras); - Timber.d("GenerateReviewFragment placed"); - } - - void startReceiveFragment(Bundle extras) { - replaceFragment(new ReceiveFragment(), null, extras); - Timber.d("ReceiveFragment placed"); - } - - void replaceFragment(Fragment newFragment, String stackName, Bundle extras) { - if (extras != null) { - newFragment.setArguments(extras); - } - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.fragment_container, newFragment); - transaction.addToBackStack(stackName); - transaction.commit(); - } - - void popFragmentStack(String name) { - getSupportFragmentManager().popBackStack(name, FragmentManager.POP_BACK_STACK_INCLUSIVE); - } - - ////////////////////////////////////////// - // GenerateFragment.Listener - ////////////////////////////////////////// - static final String MNEMONIC_LANGUAGE = "English"; // see mnemonics/electrum-words.cpp for more - - private class AsyncCreateWallet extends AsyncTask { - final String walletName; - final String walletPassword; - final WalletCreator walletCreator; - - File newWalletFile; - - public AsyncCreateWallet(final String name, final String password, - final WalletCreator walletCreator) { - super(); - this.walletName = name; - this.walletPassword = password; - this.walletCreator = walletCreator; - } - - @Override - protected void onPreExecute() { - super.onPreExecute(); - showProgressDialog(R.string.generate_wallet_creating); - } - - @Override - protected Boolean doInBackground(Void... params) { - // check if the wallet we want to create already exists - File walletFolder = getStorageRoot(); - if (!walletFolder.isDirectory()) { - Timber.e("Wallet dir " + walletFolder.getAbsolutePath() + "is not a directory"); - return false; - } - File cacheFile = new File(walletFolder, walletName); - File keysFile = new File(walletFolder, walletName + ".keys"); - File addressFile = new File(walletFolder, walletName + ".address.txt"); - - if (cacheFile.exists() || keysFile.exists() || addressFile.exists()) { - Timber.e("Some wallet files already exist for %s", cacheFile.getAbsolutePath()); - return false; - } - - newWalletFile = new File(walletFolder, walletName); - boolean success = walletCreator.createWallet(newWalletFile, walletPassword); - if (success) { - return true; - } else { - Timber.e("Could not create new wallet in %s", newWalletFile.getAbsolutePath()); - return false; - } -<<<<<<< HEAD - break; - default: - } - } - - private boolean startLoginFragment = false; - - @Override - protected void onResumeFragments() { - super.onResumeFragments(); - if (startLoginFragment) { - startLoginFragment(); - startLoginFragment = false; - } - } - - void startLoginFragment() { - // we set these here because we cannot be ceratin we have permissions for storage before - Helper.setMoneroHome(this); - Helper.initLogger(this); - Fragment fragment = new LoginFragment(); - getSupportFragmentManager().beginTransaction() - .add(R.id.fragment_container, fragment).commit(); - Timber.d("LoginFragment added"); - } - - void startGenerateFragment(String type) { - Bundle extras = new Bundle(); - extras.putString(GenerateFragment.TYPE, type); - replaceFragment(new GenerateFragment(), GENERATE_STACK, extras); - Timber.d("GenerateFragment placed"); - } - - void startReviewFragment(Bundle extras) { - replaceFragment(new GenerateReviewFragment(), null, extras); - Timber.d("GenerateReviewFragment placed"); - } - - void startReceiveFragment(Bundle extras) { - replaceFragment(new ReceiveFragment(), null, extras); - Timber.d("ReceiveFragment placed"); - } - - void replaceFragment(Fragment newFragment, String stackName, Bundle extras) { - if (extras != null) { - newFragment.setArguments(extras); - } - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.fragment_container, newFragment); - transaction.addToBackStack(stackName); - transaction.commit(); - } - - void popFragmentStack(String name) { - getSupportFragmentManager().popBackStack(name, FragmentManager.POP_BACK_STACK_INCLUSIVE); - } - - ////////////////////////////////////////// - // GenerateFragment.Listener - ////////////////////////////////////////// - static final String MNEMONIC_LANGUAGE = "English"; // see mnemonics/electrum-words.cpp for more - - private class AsyncCreateWallet extends AsyncTask { - final String walletName; - final String walletPassword; - final WalletCreator walletCreator; - - File newWalletFile; - - AsyncCreateWallet(final String name, final String password, - final WalletCreator walletCreator) { - super(); - this.walletName = name; - this.walletPassword = password; - this.walletCreator = walletCreator; -======= - } - - @Override - protected void onPostExecute(Boolean result) { - super.onPostExecute(result); - if (isDestroyed()) { - return; - } - dismissProgressDialog(); - if (result) { - startDetails(newWalletFile, walletPassword, GenerateReviewFragment.VIEW_TYPE_ACCEPT); - } else { - walletGenerateError(); - } - } - } - - public void createWallet(final String name, final String password, - final WalletCreator walletCreator) { - new AsyncCreateWallet(name, password, walletCreator) - .executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR); - } - - void walletGenerateError() { - try { - GenerateFragment genFragment = (GenerateFragment) - getSupportFragmentManager().findFragmentById(R.id.fragment_container); - genFragment.walletGenerateError(); - } catch (ClassCastException ex) { - Timber.e("walletGenerateError() but not in GenerateFragment"); - } - } - - interface WalletCreator { - boolean createWallet(File aFile, String password); - ->>>>>>> 6ce4b42f2effa3ec3525b3849c8f20ada88267d5 - } - - @Override - public void onGenerate(final String name, final String password) { - createWallet(name, password, - new WalletCreator() { - public boolean createWallet(File aFile, String password) { - Wallet newWallet = WalletManager.getInstance() - .createWallet(aFile, password, MNEMONIC_LANGUAGE); - boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok); - if (!success) { - Timber.e(newWallet.getErrorString()); - toast(newWallet.getErrorString()); - } - newWallet.close(); - return success; - } - }); - } - - @Override - public void onGenerate(final String name, final String password, final String seed, - final long restoreHeight) { - createWallet(name, password, - new WalletCreator() { - public boolean createWallet(File aFile, String password) { - Wallet newWallet = WalletManager.getInstance(). - recoveryWallet(aFile, password, seed, restoreHeight); - boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok); - if (!success) { - Timber.e(newWallet.getErrorString()); - toast(newWallet.getErrorString()); - } - newWallet.close(); - return success; - } - }); - } - - @Override - public void onGenerate(final String name, final String password, - final String address, final String viewKey, final String spendKey, - final long restoreHeight) { - createWallet(name, password, - new WalletCreator() { - public boolean createWallet(File aFile, String password) { - Wallet newWallet = WalletManager.getInstance() - .createWalletWithKeys(aFile, password, MNEMONIC_LANGUAGE, restoreHeight, - address, viewKey, spendKey); - boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok); - if (!success) { - Timber.e(newWallet.getErrorString()); - toast(newWallet.getErrorString()); - } - newWallet.close(); - return success; - } - }); - } - - void toast(final String msg) { - runOnUiThread(new Runnable() { - @Override - public void run() { - Toast.makeText(LoginActivity.this, msg, Toast.LENGTH_LONG).show(); - } - }); - } - - @Override - public void onAccept(final String name, final String password) { - File walletFolder = getStorageRoot(); - File walletFile = new File(walletFolder, name); - Timber.d("New Wallet %s", walletFile.getAbsolutePath()); - walletFile.delete(); // when recovering wallets, the cache seems corrupt - - boolean rc = 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 { - Timber.e("Wallet store failed to %s", walletFile.getAbsolutePath()); - Toast.makeText(LoginActivity.this, getString(R.string.generate_wallet_create_failed), Toast.LENGTH_LONG).show(); - } - } - - Wallet.Status testWallet(String path, String password) { - Timber.d("testing wallet %s", 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(); - Timber.d("wallet tested %s", aWallet.getStatus()); - aWallet.close(); - return status; - } - - 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, boolean ignoreCacheError) { - if (walletExists(dstWallet, true) && !overwrite) return false; - boolean success = false; - File srcDir = srcWallet.getParentFile(); - String srcName = srcWallet.getName(); - File dstDir = dstWallet.getParentFile(); - String dstName = dstWallet.getName(); - try { - try { - copyFile(new File(srcDir, srcName), new File(dstDir, dstName)); - } catch (IOException ex) { - Timber.d("CACHE %s", ignoreCacheError); - if (!ignoreCacheError) { // ignore cache backup error if backing up (can be resynced) - throw ex; - } - } - 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) { - Timber.e("wallet copy failed: %s", ex.getMessage()); - // try to rollback - deleteWallet(dstWallet); - } - return success; - } - - // do our best to delete as much as possible of the wallet files - boolean deleteWallet(File walletFile) { - Timber.d("deleteWallet %s", walletFile.getAbsolutePath()); - File dir = walletFile.getParentFile(); - String name = walletFile.getName(); - boolean success = true; - File cacheFile = new File(dir, name); - if (cacheFile.exists()) { - success = cacheFile.delete(); - } - success = new File(dir, name + ".keys").delete() && success; - File addressFile = new File(dir, name + ".address.txt"); - if (addressFile.exists()) { - success = addressFile.delete() && success; - } - Timber.d("deleteWallet is %s", success); - return success; - } - -<<<<<<< HEAD - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_create_help_new: - HelpFragment.display(getSupportFragmentManager(), R.string.help_create_new); - return true; - case R.id.action_create_help_keys: - HelpFragment.display(getSupportFragmentManager(), R.string.help_create_keys); - return true; - case R.id.action_create_help_view: - HelpFragment.display(getSupportFragmentManager(), R.string.help_create_view); - return true; - case R.id.action_create_help_seed: - HelpFragment.display(getSupportFragmentManager(), R.string.help_create_seed); - return true; - case R.id.action_details_help: - HelpFragment.display(getSupportFragmentManager(), R.string.help_details); - return true; - case R.id.action_details_changepw: - onWalletChangePassword(); - return true; - case R.id.action_license_info: - AboutFragment.display(getSupportFragmentManager()); - return true; - case R.id.action_help_list: - HelpFragment.display(getSupportFragmentManager(), R.string.help_list); - return true; - case R.id.action_privacy_policy: - PrivacyFragment.display(getSupportFragmentManager()); - return true; - case R.id.action_testnet: - try { - LoginFragment loginFragment = (LoginFragment) - getSupportFragmentManager().findFragmentById(R.id.fragment_container); - item.setChecked(loginFragment.onTestnetMenuItem()); - } catch (ClassCastException ex) { - // never mind then - } - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - public void setNetworkType(NetworkType networkType) { - WalletManager.getInstance().setNetworkType(networkType); - } -======= - 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 { - if (inChannel != null) - inChannel.close(); - if (outChannel != null) - outChannel.close(); - } - } - - @Override - public void onBackPressed() { - Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container); - if (f instanceof GenerateReviewFragment) { - if (((GenerateReviewFragment) f).backOk()) { - super.onBackPressed(); - } - } else if (f instanceof LoginFragment) { - if (((LoginFragment) f).isFabOpen()) { - ((LoginFragment) f).animateFAB(); - } else { - super.onBackPressed(); - } - } else { - super.onBackPressed(); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_create_help_new: - HelpFragment.display(getSupportFragmentManager(), R.string.help_create_new); - return true; - case R.id.action_create_help_keys: - HelpFragment.display(getSupportFragmentManager(), R.string.help_create_keys); - return true; - case R.id.action_create_help_view: - HelpFragment.display(getSupportFragmentManager(), R.string.help_create_view); - return true; - case R.id.action_create_help_seed: - HelpFragment.display(getSupportFragmentManager(), R.string.help_create_seed); - return true; - case R.id.action_details_help: - HelpFragment.display(getSupportFragmentManager(), R.string.help_details); - return true; - case R.id.action_license_info: - AboutFragment.display(getSupportFragmentManager()); - return true; - case R.id.action_help_list: - HelpFragment.display(getSupportFragmentManager(), R.string.help_list); - return true; - case R.id.action_privacy_policy: - PrivacyFragment.display(getSupportFragmentManager()); - return true; - case R.id.action_testnet: - try { - LoginFragment loginFragment = (LoginFragment) - getSupportFragmentManager().findFragmentById(R.id.fragment_container); - item.setChecked(loginFragment.onTestnetMenuItem()); - } catch (ClassCastException ex) { - } - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - private void setNet(boolean testnet) { - WalletManager.getInstance().setDaemon("", testnet, "", ""); - } ->>>>>>> 6ce4b42f2effa3ec3525b3849c8f20ada88267d5 - - static class WalletNode { - String name = null; - String host = ""; - int port = 28081; - String user = ""; - String password = ""; - boolean isTestnet; - - WalletNode(String walletName, String daemon, boolean isTestnet) { - if ((daemon == null) || daemon.isEmpty()) return; - this.name = walletName; - String daemonAddress; - String a[] = daemon.split("@"); - if (a.length == 1) { // no credentials - daemonAddress = a[0]; - } else if (a.length == 2) { // credentials - String userPassword[] = a[0].split(":"); - if (userPassword.length != 2) - throw new IllegalArgumentException("User:Password invalid"); - user = userPassword[0]; - if (!user.isEmpty()) password = userPassword[1]; - daemonAddress = a[1]; - } else { - throw new IllegalArgumentException("Too many @"); - } - - String da[] = daemonAddress.split(":"); - if ((da.length > 2) || (da.length < 1)) - throw new IllegalArgumentException("Too many ':' or too few"); - host = da[0]; - if (da.length == 2) { - try { - port = Integer.parseInt(da[1]); - } catch (NumberFormatException ex) { - throw new IllegalArgumentException("Port not numeric"); - } - } else { - port = (isTestnet ? 28081 : 18081); - } - this.isTestnet = isTestnet; - } - - String getAddress() { - return host + ":" + port; - } - - boolean isValid() { - return !host.isEmpty(); - } - } - - private class AsyncOpenWallet extends AsyncTask { - final static int OK = 0; - final static int TIMEOUT = 1; - final static int INVALID = 2; - final static int IOEX = 3; - - WalletNode walletNode; - -<<<<<<< HEAD - try { - long timeDA = new Date().getTime(); - SocketAddress address = walletNode.getSocketAddress(); - long timeDB = new Date().getTime(); - Timber.d("Resolving " + walletNode.getAddress() + " took " + (timeDB - timeDA) + "ms."); - Socket socket = new Socket(); - long timeA = new Date().getTime(); - socket.connect(address, LoginActivity.DAEMON_TIMEOUT); - socket.close(); - long timeB = new Date().getTime(); - long time = timeB - timeA; - Timber.d("Daemon " + walletNode.getAddress() + " is " + time + "ms away."); - return (time < LoginActivity.DAEMON_TIMEOUT ? OK : TIMEOUT); - } catch (IOException ex) { - Timber.d("Cannot reach daemon %s because %s", walletNode.getAddress(), ex.getMessage()); - return IOEX; - } catch (IllegalArgumentException ex) { - Timber.d("Cannot reach daemon %s because %s", walletNode.getAddress(), ex.getMessage()); - return INVALID; -======= - @Override - protected void onPreExecute() { - super.onPreExecute(); - showProgressDialog(R.string.open_progress, DAEMON_TIMEOUT / 4); ->>>>>>> 6ce4b42f2effa3ec3525b3849c8f20ada88267d5 - } - - @Override - protected Integer doInBackground(WalletNode... params) { - if (params.length != 1) return INVALID; - this.walletNode = params[0]; - if (!walletNode.isValid()) return INVALID; - - Timber.d("checking %s", walletNode.getAddress()); - - try { - long timeDA = new Date().getTime(); - SocketAddress address = new InetSocketAddress(walletNode.host, walletNode.port); - long timeDB = new Date().getTime(); - Timber.d("Resolving " + walletNode.host + " took " + (timeDB - timeDA) + "ms."); - Socket socket = new Socket(); - long timeA = new Date().getTime(); - socket.connect(address, LoginActivity.DAEMON_TIMEOUT); - socket.close(); - long timeB = new Date().getTime(); - long time = timeB - timeA; - Timber.d("Daemon " + walletNode.host + " is " + time + "ms away."); - return (time < LoginActivity.DAEMON_TIMEOUT ? OK : TIMEOUT); - } catch (IOException ex) { - Timber.d("Cannot reach daemon " + walletNode.host + "/" + walletNode.port + " because " + ex.getMessage()); - return IOEX; - } catch (IllegalArgumentException ex) { - Timber.d("Cannot reach daemon " + walletNode.host + "/" + walletNode.port + " because " + ex.getMessage()); - return INVALID; - } - } -<<<<<<< HEAD - dismissProgressDialog(); - switch (result) { - case OK: - Timber.d("selected wallet is .%s.", walletNode.getName()); - // now it's getting real, onValidateFields if wallet exists - promptAndStart(walletNode); - break; - case TIMEOUT: - Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_connect_timeout), Toast.LENGTH_LONG).show(); - break; - case INVALID: - Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_node_invalid), Toast.LENGTH_LONG).show(); - break; - case IOEX: - Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_connect_ioex), Toast.LENGTH_LONG).show(); - break; -======= - - @Override - protected void onPostExecute(Integer result) { - super.onPostExecute(result); - if (isDestroyed()) { - return; - } - dismissProgressDialog(); - switch (result) { - case OK: - Timber.d("selected wallet is ." + walletNode.name + "."); - // now it's getting real, onValidateFields if wallet exists - promptAndStart(walletNode); - break; - case TIMEOUT: - Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_connect_timeout), Toast.LENGTH_LONG).show(); - break; - case INVALID: - Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_node_invalid), Toast.LENGTH_LONG).show(); - break; - case IOEX: - Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_connect_ioex), Toast.LENGTH_LONG).show(); - break; - } ->>>>>>> 6ce4b42f2effa3ec3525b3849c8f20ada88267d5 - } - } - -<<<<<<< HEAD - void promptAndStart(WalletNode walletNode) { - File walletFile = Helper.getWalletFile(this, walletNode.getName()); - if (WalletManager.getInstance().walletExists(walletFile)) { - WalletManager.getInstance().setDaemon(walletNode); - promptPassword(walletNode.getName(), new PasswordAction() { - @Override - public void action(String walletName, String password) { - startWallet(walletName, password); - } - }); - } else { // this cannot really happen as we prefilter choices - Toast.makeText(this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show(); -======= - void promptAndStart(WalletNode walletNode) { - File walletFile = Helper.getWalletFile(this, walletNode.name); - if (WalletManager.getInstance().walletExists(walletFile)) { - WalletManager.getInstance(). - setDaemon(walletNode.getAddress(), walletNode.isTestnet, walletNode.user, walletNode.password); - promptPassword(walletNode.name, new PasswordAction() { - @Override - public void action(String walletName, String password) { - startWallet(walletName, password); - } - }); - } else { // this cannot really happen as we prefilter choices - Toast.makeText(this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show(); - } ->>>>>>> 6ce4b42f2effa3ec3525b3849c8f20ada88267d5 - } -} diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml deleted file mode 100644 index b11d7d99e3..0000000000 --- a/app/src/main/res/values-es/strings.xml +++ /dev/null @@ -1,350 +0,0 @@ - - Monedero - - Testnet - Acerca De - Política de Privacidad - - Compartir - Ayuda - Detalles - Recibir - Renombrar … - Archivar - Copia de seguridad - [Change Passphrase] - - Sigue escribiendo … - Mas o menos. - Puedes hacerlo mejor. - Casi … - ¡Bien ahí, hacker nivel 4! - - Monederos - Créditos - Aceptar - Cancelar - Cerrar - Toca para información más detallada - - ¡Éxito! - Hecho - - Toca para mostrar código QR - Mayor Prioridad = Mayor Comisión - - ID de pago integrado - Preparando tu transacción - - ¡ID de transacción copiado al portapapeles! - - ID de Transacción - Dirección de Destino - ID de Pago - Notas - - Copia de seguridad en progreso - Archivado en progreso - Cambio de nombre en progreso - Comprobando conexión con el daemon - [Change Password in progress] - - Guardando todo\n¡Puede llevar un tiempo! - - Copia de seguridad satisfactoria - ¡Copia de seguridad fallida! - Archivado satisfactorio - ¡Archivado fallido! - ¡Borrado fallido! - ¡Cambio de nombre fallido! - [Change Password failed!] - [Password changed] - - Nodo - ([<user>:<pass>@]<daemon>[:<port>]) - Selección de Red - StageNet - TestNet - MainNet - Cargando lista de monederos - Cargando monedero… - Guardando monedero - Monedero guardada - ¡Guardado de monedero fallido! - Conectando… - ¡Conexión con el nodo fallida!\nComprueba el usuario/contraseña - ¡Conexión con el nodo ha expirado!\nInténtalo de nuevo o prueba otro. - ¡Nodo inválido!\nInténtalo con otro. - ¡No se puede alcanzar el nodo!\nInténtalo de nuevo o prueba otro. - - Trabajando en ello … - Desconectado - - ¡Transacción enviada! - Transacción fallida: %1$s - ¡No se pudo crear la transacción! - - Todavía estoy ocupado con tu última monedero … - - Renombrar %1$s - - [New Passphrase for %1$s] - [Repeat Passphrase for %1$s] - Contraseña para %1$s - Confirmar Contraseña - ¡Contraseña incorrecta! - ¡El monedero no existe! - ¡Esto no es un monedero! - ¡La dirección del daemon debe estar configurada! - El monedero no coincide con la red seleccionada - ¡No se puede conectar al daemon! Inténtalo de nuevo. - ¡Algo ha sucedido! - - (Sólo Vista) - - Recibir - Enviar - - Monto - Fecha - + %1$s XMR sin confirmar - Transacciones - ¡Conectado al daemon! - - Servicio de monerujo - Servicio de monerujo en ejecucion - Servicio de monerujo detenido - Servicio de monerujo - Servicio de monerujo conectado - Servicio de monerujo desconectado - - Sincronizado: - bloques restantes - Escaneando: - - Problemas - ¡No se puede escribir en el almacenamiento externo! ¡Pánico! - ¡De verdad necesitamos esos permisos para el almacenamiento externo! - Sin cámara = ¡Sin escaneo de QR! - - Clave de Vista - Dirección Pública - ¡Clave de vista copiada al portapapeles! - ¡Dirección del monedero copiada al portapapeles! - ¡La selección de la semilla está desactivada por motivos de seguridad! - ¡La selección de la clave de gasto está desactivada por motivos de seguridad! - ¡La copia está desactivada por motivos de seguridad! - - ¡No se ha podido obtener la tasa de cambio!\nUsa XMR/XMR o inténtalo de nuevo - - Crear monedero - Nombre del monedero - Frase de Contraseña - [Passphrases do not match] - [Passphrase may not be empty] - ¡Házme ya un monedero! - Semilla Mnemotécnica - [I have noted the above info!] - - Copia de Seguridad - Exportar Claves - BORRAR - - ¡Dame un nombre! - ¡El monedero ya existe! - No puede empezar con . - Creando monedero - Monedero creada - Creación de monedero fallida - - Introduce un número o una fecha (AAAA-MM-DD) - - Claves - Nuevo - Semilla - Ver - - Dirección Pública - Clave de Vista - Clave de Gasto - Semilla Mnemotécnica de 25 Palabras - Altura o Fecha (YYYY-MM-DD) de Restauración - - Monedero - Contraseña - Dirección Pública - Clave de Vista - Clave de Gasto - Semilla Mnemotécnica - Altura de Restauración: - [Wallet Files Restore Password] - - Introduce una clave válida - Introduce una dirección válida - Introduce tu semilla de 25 palabras - - Dirección XMR o BTC del Destinatario - ID de Pago (opcional) - 0.00 - Notas Privadas (opcional) - Prioridad de Transacción - Mezclar - Barrer - Generar - Scan - Preparar - Disponer (Deshacer) - Último Paso - ¡Confirma! - No es un Código QR de monero - Dirección de Monero inválida - Preparando transacción - Enviar - Fondos disponibles: %1$s XMR - Dirección - Monto - Ajustes - Aprobar - Hecho - Resumen - Monto > Fondos - - Monto - Comisión - Polvo - Total - - Error creando la transacción - - incl. comisión de %1$s - - Comisión %1$s - (%1$s) - fallido - - %1$s - + %1$s - - Dirección - Marca de tiempo - ID de Transacción - Clave de Transacción - Destino - ID de Pago - Bloque - Monto - Comisión - Transferencias - Notas - Copiar al Portapapeles - Copiado al Portapapeles - Detalles de la transacción - (opcional) - Guardar - Notas guardadas - El guardado de las notas ha fallado - Detalles de la Transacción - - PENDIENTE - FALLIDO - - Muéstrame el código QR - Generar - ID de Pago (opcional) - Monto - XMR - ¡No se ha podido abrir el monedero! - 16 o 64 caracteres hexadecimales (0–9,a–f) - Debe estar vacío con una dirección integrada - Debe ser > 0 - - Introduce valor - Max. %1$s - Min. 0 - XMR no es un número - - Recibir - - Se va a mostrar información delicada.\n¡Mira por encima del hombro! - Estoy seguro - ¡Llévame de vuelta! - Detalles - - ¡El monedero será copiada y después borrada! - ¡Sí, haz eso! - ¡No gracias! - - - Prioridad por Defecto - Prioridad Baja - Prioridad Media - Prioridad Alta - - - Crear nuevo monedero - Restaurar monedero de sólo vista - Restaurar monedero con claves privadas - Restaurar monedero con semilla de 25 palabras - Ingresaste una dirección Bitcoin
- Vas a enviar XMR y el destinatario recibirá BTC usando el servicio XMR.TO. - ]]>
- Enviando - %1$s BTC - Clave: - Dirección: - Estado: - %1$s BTC - Confirmación pendiente - Pago pendiente - Error de XMR.TO (%1$s) - BTC Enviados! - Consultando … - Puedes enviar %1$s — %2$s BTC.
- XMR.TO está ofreciendo una tasa de cambio de %3$s BTC en este momento. - ]]>
- Montos hasta %1$s BTC serán enviados en el momento! - ]]> - Saldo: %2$s BTC (%1$s XMR) - Creando orden XMR.TO - Consultando orden XMR.TO - Preparando transacción Monero - Consultando parámetros XMR.TO - ERROR XMR.TO - Código: %1$d - Toca para reintentar - Parece que estamos atascados! - Oh-oh, parece que XMR.TO no está disponible ahora! - %1$s BTC = %2$s XMR - (Cambio: %1$s BTC/XMR) - Avanzado - Click para más detalles - Visita https://xmr.to para soporte y rastreo - Mira en los datos de la TX para más detalles - Clave secreta\nXMR.TO - Clave secreta XMR.TO - Dirección BTC destino - Monto - Creando orden XMR.TO - Toca para reintentar - Oye, esperaste demasiado! - Clave XMR.TO - ¡Clave XMR.TO copiada al portapapeles! - %1$s (indicativo) - Enviar mis preciados moneroj - Gastar mis preciados moneroj (%1$s) - No es una dirección válida - Comisión (XMR) - Total (XMR) - %1$s XMR - +%1$s Comisión - Estado - TX ID\n(BTC) - Destino\n(BTC) - Monto\n(BTC) - Debe quedar vacío con una dirección Bitcoin - Soy monerujo - Orden XMR.TO - Pago en BTC activado, toca para más info. - Backup To File - Backup To NFC -
From e2a33bfe87987dc877b5add6c718f1aaab2993ff Mon Sep 17 00:00:00 2001 From: "jianjunchu@gmail.com" Date: Wed, 2 May 2018 20:19:52 +0800 Subject: [PATCH 04/12] update --- .idea/misc.xml | 25 + .../xmrwallet/GenerateReviewFragment.java | 1328 ++++++++++++----- .../com/m2049r/xmrwallet/LoginFragment.java | 2 +- 3 files changed, 963 insertions(+), 392 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index 7bfef59df1..c0f68eddd7 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,30 @@ + + + + diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java index cabcb83088..b25c6f1048 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java @@ -16,36 +16,40 @@ 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.media.MediaScannerConnection; import android.os.AsyncTask; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.design.widget.TextInputLayout; +import android.os.Handler; +import android.support.annotation.NonNull; import android.support.v4.app.Fragment; -import android.text.Editable; -import android.text.Html; -import android.text.TextWatcher; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; import android.view.KeyEvent; import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; -import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; -import android.widget.Button; -import android.widget.ImageButton; -import android.widget.LinearLayout; -import android.widget.ProgressBar; -import android.widget.ScrollView; -import android.widget.Switch; +import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; +import com.m2049r.xmrwallet.data.WalletNode; +import com.m2049r.xmrwallet.dialog.AboutFragment; +import com.m2049r.xmrwallet.dialog.CreditsFragment; +import com.m2049r.xmrwallet.dialog.HelpFragment; +import com.m2049r.xmrwallet.dialog.PrivacyFragment; import com.m2049r.xmrwallet.model.NetworkType; import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.model.WalletManager; +import com.m2049r.xmrwallet.service.WalletService; import com.m2049r.xmrwallet.util.FingerprintHelper; import com.m2049r.xmrwallet.util.Helper; import com.m2049r.xmrwallet.util.KeyStoreHelper; @@ -53,513 +57,1055 @@ import com.m2049r.xmrwallet.widget.Toolbar; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.Socket; +import java.net.SocketAddress; +import java.nio.channels.FileChannel; import java.security.KeyStoreException; +import java.util.Date; import timber.log.Timber; -public class GenerateReviewFragment extends Fragment { - static final public String VIEW_TYPE_DETAILS = "details"; - static final public String VIEW_TYPE_ACCEPT = "accept"; - static final public String VIEW_TYPE_WALLET = "wallet"; - - ScrollView scrollview; - - ProgressBar pbProgress; - TextView tvWalletPassword; - TextView tvWalletAddress; - TextView tvWalletMnemonic; - TextView tvWalletViewKey; - TextView tvWalletSpendKey; - ImageButton bCopyAddress; - LinearLayout llAdvancedInfo; - LinearLayout llPassword; - Button bAdvancedInfo; - Button bAccept; - - // TODO fix visibility of variables - String walletPath; - String walletName; - // we need to keep the password so the user is not asked again if they want to change it - // note they can only enter this fragment immediately after entering the password - // so asking them to enter it a couple of seconds later seems silly - String walletPassword = null; +public class LoginActivity extends SecureActivity + implements LoginFragment.Listener, GenerateFragment.Listener, + GenerateReviewFragment.Listener, GenerateReviewFragment.AcceptListener, + GenerateReviewFragment.ProgressListener, ReceiveFragment.Listener { + private static final String GENERATE_STACK = "gen"; + + static final int DAEMON_TIMEOUT = 500; // deamon must respond in 500ms + + private Toolbar toolbar; @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - - View view = inflater.inflate(R.layout.fragment_review, container, false); - - scrollview = (ScrollView) view.findViewById(R.id.scrollview); - pbProgress = (ProgressBar) view.findViewById(R.id.pbProgress); - 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); - bCopyAddress = (ImageButton) view.findViewById(R.id.bCopyAddress); - bAdvancedInfo = (Button) view.findViewById(R.id.bAdvancedInfo); - llAdvancedInfo = (LinearLayout) view.findViewById(R.id.llAdvancedInfo); - llPassword = (LinearLayout) view.findViewById(R.id.llPassword); - - bAccept = (Button) view.findViewById(R.id.bAccept); - - boolean testnet = WalletManager.getInstance().getNetworkType() != NetworkType.NetworkType_Mainnet; - tvWalletMnemonic.setTextIsSelectable(testnet); - tvWalletSpendKey.setTextIsSelectable(testnet); - tvWalletPassword.setTextIsSelectable(testnet); - - bAccept.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - acceptWallet(); - } - }); - view.findViewById(R.id.bCopyViewKey).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - copyViewKey(); - } - }); - bCopyAddress.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - copyAddress(); - } - }); - bCopyAddress.setClickable(false); - view.findViewById(R.id.bAdvancedInfo).setOnClickListener(new View.OnClickListener() { + public void setToolbarButton(int type) { + toolbar.setButton(type); + } + + @Override + public void setTitle(String title) { + toolbar.setTitle(title); + } + + @Override + public void setSubtitle(String subtitle) { + toolbar.setSubtitle(subtitle); + } + + @Override + public void setTitle(String title, String subtitle) { + toolbar.setTitle(title, subtitle); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + Timber.d("onCreate()"); + super.onCreate(savedInstanceState); + if (savedInstanceState != null) { + // we don't store anything ourselves + } + + setContentView(R.layout.activity_login); + toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayShowTitleEnabled(false); + + toolbar.setOnButtonListener(new Toolbar.OnButtonListener() { @Override - public void onClick(View v) { - showAdvancedInfo(); + public void onButton(int type) { + switch (type) { + case Toolbar.BUTTON_BACK: + onBackPressed(); + break; + case Toolbar.BUTTON_CLOSE: + finish(); + break; + case Toolbar.BUTTON_CREDITS: + CreditsFragment.display(getSupportFragmentManager()); + break; + case Toolbar.BUTTON_NONE: + default: + Timber.e("Button " + type + "pressed - how can this be?"); + } } }); - Bundle args = getArguments(); - type = args.getString("type"); - walletPath = args.getString("path"); - showDetails(args.getString("password")); - return view; + if (Helper.getWritePermission(this)) { + if (savedInstanceState == null) startLoginFragment(); + } else { + Timber.i("Waiting for permissions"); + } } - void showDetails(String password) { - walletPassword = password; - showProgress(); - tvWalletPassword.setText(null); - new AsyncShow().executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR, walletPath); + boolean checkServiceRunning() { + if (WalletService.Running) { + Toast.makeText(this, getString(R.string.service_busy), Toast.LENGTH_SHORT).show(); + return true; + } else { + return false; + } } - void copyViewKey() { - Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_viewkey), tvWalletViewKey.getText().toString()); - Toast.makeText(getActivity(), getString(R.string.message_copy_viewkey), Toast.LENGTH_SHORT).show(); + @Override + public boolean onWalletSelected(String walletName, String daemon) { + if (daemon.length() == 0) { + Toast.makeText(this, getString(R.string.prompt_daemon_missing), Toast.LENGTH_SHORT).show(); + return false; + } + if (checkServiceRunning()) return false; + try { + WalletNode aWalletNode = new WalletNode(walletName, daemon, WalletManager.getInstance().getNetworkType()); + new AsyncOpenWallet().execute(aWalletNode); + } catch (IllegalArgumentException ex) { + Timber.e(ex.getLocalizedMessage()); + Toast.makeText(this, ex.getLocalizedMessage(), Toast.LENGTH_SHORT).show(); + return false; + } + return true; } - void copyAddress() { - Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_address), tvWalletAddress.getText().toString()); - Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show(); + @Override + public void onWalletDetails(final String walletName) { + Timber.d("details for wallet .%s.", walletName); + if (checkServiceRunning()) return; + DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + final File walletFile = Helper.getWalletFile(LoginActivity.this, walletName); + if (WalletManager.getInstance().walletExists(walletFile)) { + Helper.promptPassword(LoginActivity.this, walletName, true, new Helper.PasswordAction() { + @Override + public void action(String walletName, String password, boolean fingerprintUsed) { + startDetails(walletFile, password, GenerateReviewFragment.VIEW_TYPE_DETAILS); + } + }); + } else { // this cannot really happen as we prefilter choices + Timber.e("Wallet missing: %s", walletName); + Toast.makeText(LoginActivity.this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show(); + } + break; + + case DialogInterface.BUTTON_NEGATIVE: + // do nothing + break; + } + } + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage(getString(R.string.details_alert_message)) + .setPositiveButton(getString(R.string.details_alert_yes), dialogClickListener) + .setNegativeButton(getString(R.string.details_alert_no), dialogClickListener) + .show(); } - void nocopy() { - Toast.makeText(getActivity(), getString(R.string.message_nocopy), Toast.LENGTH_SHORT).show(); + @Override + public void onWalletReceive(String walletName) { + Timber.d("receive for wallet .%s.", walletName); + if (checkServiceRunning()) return; + final File walletFile = Helper.getWalletFile(this, walletName); + if (WalletManager.getInstance().walletExists(walletFile)) { + Helper.promptPassword(LoginActivity.this, walletName, false, new Helper.PasswordAction() { + @Override + public void action(String walletName, String password, boolean fingerprintUsed) { + startReceive(walletFile, password); + } + }); + } else { // this cannot really happen as we prefilter choices + Toast.makeText(this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show(); + } } - void showAdvancedInfo() { - llAdvancedInfo.setVisibility(View.VISIBLE); - bAdvancedInfo.setVisibility(View.GONE); - scrollview.post(new Runnable() { - @Override - public void run() { - scrollview.fullScroll(ScrollView.FOCUS_DOWN); + private class AsyncRename extends AsyncTask { + @Override + protected void onPreExecute() { + super.onPreExecute(); + showProgressDialog(R.string.rename_progress); + } + + @Override + protected Boolean doInBackground(String... params) { + if (params.length != 2) return false; + File walletFile = Helper.getWalletFile(LoginActivity.this, params[0]); + String newName = params[1]; + boolean success = renameWallet(walletFile, newName); + try { + if (success && FingerprintHelper.isFingerprintAuthAllowed(params[0])) { + String savedPass = KeyStoreHelper.loadWalletUserPass(LoginActivity.this, params[0]); + KeyStoreHelper.saveWalletUserPass(LoginActivity.this, newName, savedPass); + KeyStoreHelper.removeWalletUserPass(LoginActivity.this, params[0]); + } + } catch (KeyStoreException ex) { + ex.printStackTrace(); } - }); + return success; + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + if (isDestroyed()) { + return; + } + dismissProgressDialog(); + if (result) { + reloadWalletList(); + } else { + Toast.makeText(LoginActivity.this, getString(R.string.rename_failed), Toast.LENGTH_LONG).show(); + } + } + } + + // copy + delete seems safer than rename because we call rollback easily + boolean renameWallet(File walletFile, String newName) { + if (copyWallet(walletFile, new File(walletFile.getParentFile(), newName), false, true)) { + deleteWallet(walletFile); + return true; + } else { + return false; + } } - String type; + @Override + public void onWalletRename(final String walletName) { + Timber.d("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(getString(R.string.label_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(getString(R.string.label_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); - private void acceptWallet() { - bAccept.setEnabled(false); - acceptCallback.onAccept(walletName, walletPassword); + // 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 AsyncShow extends AsyncTask { - String name; - String address; - String seed; - String viewKey; - String spendKey; - boolean isWatchOnly; - Wallet.Status status; + private class AsyncBackup extends AsyncTask { + @Override + protected void onPreExecute() { + super.onPreExecute(); + showProgressDialog(R.string.backup_progress); + } @Override protected Boolean doInBackground(String... params) { if (params.length != 1) return false; - String walletPath = params[0]; + return backupWallet(params[0]); + } - Wallet wallet; - boolean closeWallet; - if (type.equals(GenerateReviewFragment.VIEW_TYPE_WALLET)) { - wallet = GenerateReviewFragment.this.walletCallback.getWallet(); - closeWallet = false; - } else { - wallet = WalletManager.getInstance().openWallet(walletPath, walletPassword); - closeWallet = true; + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + if (isDestroyed()) { + return; + } + dismissProgressDialog(); + if (!result) { + Toast.makeText(LoginActivity.this, getString(R.string.backup_failed), Toast.LENGTH_LONG).show(); } - name = wallet.getName(); - status = wallet.getStatus(); - if (status != Wallet.Status.Status_Ok) { - Timber.e(wallet.getErrorString()); - if (closeWallet) wallet.close(); + } + } + + private boolean backupWallet(String walletName) { + File backupFolder = new File(getStorageRoot(), "backups"); + if (!backupFolder.exists()) { + if (!backupFolder.mkdir()) { + Timber.e("Cannot create backup dir %s", backupFolder.getAbsolutePath()); return false; } + // make folder visible over USB/MTP + MediaScannerConnection.scanFile(this, new String[]{backupFolder.toString()}, null, null); + } + File walletFile = Helper.getWalletFile(LoginActivity.this, walletName); + File backupFile = new File(backupFolder, walletName); + Timber.d("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 + boolean success = copyWallet(walletFile, backupFile, true, true); + Timber.d("copyWallet is %s", success); + return success; + } - address = wallet.getAddress(); - seed = wallet.getSeed(); - viewKey = wallet.getSecretViewKey(); - spendKey = isWatchOnly ? getActivity().getString(R.string.label_watchonly) : wallet.getSecretSpendKey(); - isWatchOnly = wallet.isWatchOnly(); - if (closeWallet) wallet.close(); - return true; + @Override + public void onWalletBackup(String walletName) { + Timber.d("backup for wallet ." + walletName + "."); + new AsyncBackup().execute(walletName); + } + + private class AsyncArchive extends AsyncTask { + @Override + protected void onPreExecute() { + super.onPreExecute(); + showProgressDialog(R.string.archive_progress); + } + + @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))) { + KeyStoreHelper.removeWalletUserPass(LoginActivity.this, walletName); + return true; + } else { + return false; + } } @Override protected void onPostExecute(Boolean result) { super.onPostExecute(result); - if (!isAdded()) return; // never mind - walletName = name; + if (isDestroyed()) { + return; + } + dismissProgressDialog(); if (result) { - if (type.equals(GenerateReviewFragment.VIEW_TYPE_ACCEPT)) { - bAccept.setVisibility(View.VISIBLE); - bAccept.setEnabled(true); - } - if (walletPassword != null) { - llPassword.setVisibility(View.VISIBLE); - tvWalletPassword.setText(walletPassword); - } - tvWalletAddress.setText(address); - tvWalletMnemonic.setText(seed); - tvWalletViewKey.setText(viewKey); - tvWalletSpendKey.setText(spendKey); - bAdvancedInfo.setVisibility(View.VISIBLE); - bCopyAddress.setClickable(true); - bCopyAddress.setImageResource(R.drawable.ic_content_copy_black_24dp); - activityCallback.setTitle(name, getString(R.string.details_title)); - activityCallback.setToolbarButton( - GenerateReviewFragment.VIEW_TYPE_ACCEPT.equals(type) ? Toolbar.BUTTON_NONE : Toolbar.BUTTON_BACK); + reloadWalletList(); } else { - // TODO show proper error message and/or end the fragment? - tvWalletAddress.setText(status.toString()); - tvWalletMnemonic.setText(status.toString()); - tvWalletViewKey.setText(status.toString()); - tvWalletSpendKey.setText(status.toString()); + Toast.makeText(LoginActivity.this, getString(R.string.archive_failed), Toast.LENGTH_LONG).show(); } - hideProgress(); } } - Listener activityCallback = null; - ProgressListener progressCallback = null; - AcceptListener acceptCallback = null; - ListenerWithWallet walletCallback = null; + @Override + public void onWalletArchive(final String walletName) { + Timber.d("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(); + } - public interface Listener { - void setTitle(String title, String subtitle); + void reloadWalletList() { + Timber.d("reloadWalletList()"); + try { + LoginFragment loginFragment = (LoginFragment) + getSupportFragmentManager().findFragmentById(R.id.fragment_container); + if (loginFragment != null) { + loginFragment.loadList(); + } + } catch (ClassCastException ex) { + } + } + + public void onWalletChangePassword() {//final String walletName, final String walletPassword) { + try { + GenerateReviewFragment detailsFragment = (GenerateReviewFragment) + getSupportFragmentManager().findFragmentById(R.id.fragment_container); + AlertDialog dialog = detailsFragment.createChangePasswordDialog(); + if (dialog != null) { + Helper.showKeyboard(dialog); + dialog.show(); + } + } catch (ClassCastException ex) { + Timber.w("onWalletChangePassword() called, but no GenerateReviewFragment active"); + } + } - void setToolbarButton(int type); + @Override + public void onAddWallet(String type) { + if (checkServiceRunning()) return; + startGenerateFragment(type); } - public interface ProgressListener { - void showProgressDialog(int msgId); + //////////////////////////////////////// + // LoginFragment.Listener + //////////////////////////////////////// + @Override + public SharedPreferences getPrefs() { + return getPreferences(Context.MODE_PRIVATE); + } - void dismissProgressDialog(); + @Override + public File getStorageRoot() { + return Helper.getWalletRoot(getApplicationContext()); } + //////////////////////////////////////// + //////////////////////////////////////// - public interface AcceptListener { - void onAccept(String name, String password); + @Override + public void showNet() { + switch (WalletManager.getInstance().getNetworkType()) { + case NetworkType_Mainnet: + toolbar.setSubtitle(getString(R.string.connect_mainnet)); + toolbar.setBackgroundResource(R.drawable.backgound_toolbar_mainnet); + break; + case NetworkType_Testnet: + toolbar.setSubtitle(getString(R.string.connect_testnet)); + toolbar.setBackgroundResource(R.color.colorPrimaryDark); + break; + case NetworkType_Stagenet: + toolbar.setSubtitle(getString(R.string.connect_stagenet)); + toolbar.setBackgroundResource(R.color.colorPrimaryDark); + break; + default: + throw new IllegalStateException("NetworkType unknown: " + WalletManager.getInstance().getNetworkType()); + } } - public interface ListenerWithWallet { - Wallet getWallet(); + @Override + protected void onPause() { + Timber.d("onPause()"); + super.onPause(); } + ProgressDialog progressDialog = null; + @Override - public void onAttach(Context context) { - super.onAttach(context); - if (context instanceof Listener) { - this.activityCallback = (Listener) context; - } - if (context instanceof ProgressListener) { - this.progressCallback = (ProgressListener) context; - } - if (context instanceof AcceptListener) { - this.acceptCallback = (AcceptListener) context; + public void showProgressDialog(int msgId) { + showProgressDialog(msgId, 0); + } + + private void showProgressDialog(int msgId, long delay) { + dismissProgressDialog(); // just in case + progressDialog = new MyProgressDialog(LoginActivity.this, msgId); + if (delay > 0) { + Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + public void run() { + if (progressDialog != null) progressDialog.show(); + } + }, delay); + } else { + progressDialog.show(); } - if (context instanceof ListenerWithWallet) { - this.walletCallback = (ListenerWithWallet) context; + } + + @Override + public void dismissProgressDialog() { + if (progressDialog != null && progressDialog.isShowing()) { + progressDialog.dismiss(); } + progressDialog = null; } @Override - public void onResume() { + protected void onDestroy() { + dismissProgressDialog(); + super.onDestroy(); + } + + @Override + protected void onResume() { super.onResume(); Timber.d("onResume()"); - activityCallback.setTitle(walletName, getString(R.string.details_title)); - activityCallback.setToolbarButton( - GenerateReviewFragment.VIEW_TYPE_ACCEPT.equals(type) ? Toolbar.BUTTON_NONE : Toolbar.BUTTON_BACK); + // wait for WalletService to finish + if (WalletService.Running && (progressDialog == null)) { + // and show a progress dialog, but only if there isn't one already + new AsyncWaitForService().execute(); + } } - public void showProgress() { - pbProgress.setVisibility(View.VISIBLE); + private class MyProgressDialog extends ProgressDialog { + Activity activity; + + MyProgressDialog(Activity activity, int msgId) { + super(activity); + this.activity = activity; + setCancelable(false); + setMessage(activity.getString(msgId)); + } + + @Override + public void onBackPressed() { + // prevent back button + } + } + + + private class AsyncWaitForService extends AsyncTask { + @Override + protected void onPreExecute() { + super.onPreExecute(); + showProgressDialog(R.string.service_progress); + } + + @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); + if (isDestroyed()) { + return; + } + dismissProgressDialog(); + } } - public void hideProgress() { - pbProgress.setVisibility(View.GONE); + void startWallet(String walletName, String walletPassword, boolean fingerprintUsed) { + Timber.d("startWallet()"); + Intent intent = new Intent(getApplicationContext(), WalletActivity.class); + intent.putExtra(WalletActivity.REQUEST_ID, walletName); + intent.putExtra(WalletActivity.REQUEST_PW, walletPassword); + intent.putExtra(WalletActivity.REQUEST_FINGERPRINT_USED, fingerprintUsed); + startActivity(intent); } - boolean backOk() { - return !type.equals(GenerateReviewFragment.VIEW_TYPE_ACCEPT); + void startDetails(File walletFile, String password, String type) { + Timber.d("startDetails()"); + Bundle b = new Bundle(); + b.putString("path", walletFile.getAbsolutePath()); + b.putString("password", password); + b.putString("type", type); + startReviewFragment(b); } - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setHasOptionsMenu(true); + void startReceive(File walletFile, String password) { + Timber.d("startReceive()"); + Bundle b = new Bundle(); + b.putString("path", walletFile.getAbsolutePath()); + b.putString("password", password); + startReceiveFragment(b); } @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - String type = getArguments().getString("type"); - if (GenerateReviewFragment.VIEW_TYPE_ACCEPT.equals(type)) { - inflater.inflate(R.menu.wallet_details_help_menu, menu); - super.onCreateOptionsMenu(menu, inflater); - } else { - inflater.inflate(R.menu.wallet_details_menu, menu); - super.onCreateOptionsMenu(menu, inflater); + public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { + Timber.d("onRequestPermissionsResult()"); + switch (requestCode) { + case Helper.PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE: + // If request is cancelled, the result arrays are empty. + if (grantResults.length > 0 + && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + startLoginFragment = true; + } else { + String msg = getString(R.string.message_strorage_not_permitted); + Timber.e(msg); + Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); + } + break; + default: } } - boolean changeWalletPassword(String newPassword) { - Wallet wallet; - boolean closeWallet; - if (type.equals(GenerateReviewFragment.VIEW_TYPE_WALLET)) { - wallet = GenerateReviewFragment.this.walletCallback.getWallet(); - closeWallet = false; - } else { - wallet = WalletManager.getInstance().openWallet(walletPath, walletPassword); - closeWallet = true; + private boolean startLoginFragment = false; + + @Override + protected void onResumeFragments() { + super.onResumeFragments(); + if (startLoginFragment) { + startLoginFragment(); + startLoginFragment = false; } + } - boolean ok = false; - if (wallet.getStatus() == Wallet.Status.Status_Ok) { - wallet.setPassword(newPassword); - wallet.store(); - ok = true; - } else { - Timber.e(wallet.getErrorString()); + void startLoginFragment() { + // we set these here because we cannot be ceratin we have permissions for storage before + Helper.setMoneroHome(this); + Helper.initLogger(this); + Fragment fragment = new LoginFragment(); + getSupportFragmentManager().beginTransaction() + .add(R.id.fragment_container, fragment).commit(); + Timber.d("LoginFragment added"); + } + + void startGenerateFragment(String type) { + Bundle extras = new Bundle(); + extras.putString(GenerateFragment.TYPE, type); + replaceFragment(new GenerateFragment(), GENERATE_STACK, extras); + Timber.d("GenerateFragment placed"); + } + + void startReviewFragment(Bundle extras) { + replaceFragment(new GenerateReviewFragment(), null, extras); + Timber.d("GenerateReviewFragment placed"); + } + + void startReceiveFragment(Bundle extras) { + replaceFragment(new ReceiveFragment(), null, extras); + Timber.d("ReceiveFragment placed"); + } + + void replaceFragment(Fragment newFragment, String stackName, Bundle extras) { + if (extras != null) { + newFragment.setArguments(extras); } - if (closeWallet) wallet.close(); - return ok; + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.fragment_container, newFragment); + transaction.addToBackStack(stackName); + transaction.commit(); + } + + void popFragmentStack(String name) { + getSupportFragmentManager().popBackStack(name, FragmentManager.POP_BACK_STACK_INCLUSIVE); } - private class AsyncChangePassword extends AsyncTask { - String newPassword; + ////////////////////////////////////////// + // GenerateFragment.Listener + ////////////////////////////////////////// + static final String MNEMONIC_LANGUAGE = "English"; // see mnemonics/electrum-words.cpp for more + + private class AsyncCreateWallet extends AsyncTask { + final String walletName; + final String walletPassword; + final WalletCreator walletCreator; + + File newWalletFile; + + AsyncCreateWallet(final String name, final String password, + final WalletCreator walletCreator) { + super(); + this.walletName = name; + this.walletPassword = password; + this.walletCreator = walletCreator; + } @Override protected void onPreExecute() { super.onPreExecute(); - if (progressCallback != null) - progressCallback.showProgressDialog(R.string.changepw_progress); + showProgressDialog(R.string.generate_wallet_creating); } @Override - protected Boolean doInBackground(String... params) { - if (params.length != 4) return false; - File walletFile = Helper.getWalletFile(getActivity(), params[0]); - String oldPassword = params[1]; - String userPassword = params[2]; - boolean fingerprintAuthAllowed = Boolean.valueOf(params[3]); - newPassword = KeyStoreHelper.getCrazyPass(getActivity(), userPassword); - boolean success = changeWalletPassword(newPassword); + protected Boolean doInBackground(Void... params) { + // check if the wallet we want to create already exists + File walletFolder = getStorageRoot(); + if (!walletFolder.isDirectory()) { + Timber.e("Wallet dir " + walletFolder.getAbsolutePath() + "is not a directory"); + return false; + } + File cacheFile = new File(walletFolder, walletName); + File keysFile = new File(walletFolder, walletName + ".keys"); + File addressFile = new File(walletFolder, walletName + ".address.txt"); + + if (cacheFile.exists() || keysFile.exists() || addressFile.exists()) { + Timber.e("Some wallet files already exist for %s", cacheFile.getAbsolutePath()); + return false; + } + + newWalletFile = new File(walletFolder, walletName); + boolean success = walletCreator.createWallet(newWalletFile, walletPassword); if (success) { - if (fingerprintAuthAllowed) { - KeyStoreHelper.saveWalletUserPass(getActivity(), walletName, userPassword); - } else { - KeyStoreHelper.removeWalletUserPass(getActivity(), walletName); - } + return true; + } else { + Timber.e("Could not create new wallet in %s", newWalletFile.getAbsolutePath()); + return false; } - return success; } @Override protected void onPostExecute(Boolean result) { super.onPostExecute(result); - if (getActivity().isDestroyed()) { + if (isDestroyed()) { return; } - if (progressCallback != null) - progressCallback.dismissProgressDialog(); + dismissProgressDialog(); if (result) { - Toast.makeText(getActivity(), getString(R.string.changepw_success), Toast.LENGTH_SHORT).show(); - showDetails(newPassword); + startDetails(newWalletFile, walletPassword, GenerateReviewFragment.VIEW_TYPE_ACCEPT); } else { - Toast.makeText(getActivity(), getString(R.string.changepw_failed), Toast.LENGTH_LONG).show(); + walletGenerateError(); } } } - AlertDialog openDialog = null; // for preventing opening of multiple dialogs + public void createWallet(final String name, final String password, + final WalletCreator walletCreator) { + new AsyncCreateWallet(name, password, walletCreator) + .executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR); + } - public AlertDialog createChangePasswordDialog() { - if (openDialog != null) return null; // we are already open - LayoutInflater li = LayoutInflater.from(getActivity()); - View promptsView = li.inflate(R.layout.prompt_changepw, null); + void walletGenerateError() { + try { + GenerateFragment genFragment = (GenerateFragment) + getSupportFragmentManager().findFragmentById(R.id.fragment_container); + genFragment.walletGenerateError(); + } catch (ClassCastException ex) { + Timber.e("walletGenerateError() but not in GenerateFragment"); + } + } - AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity()); - alertDialogBuilder.setView(promptsView); + interface WalletCreator { + boolean createWallet(File aFile, String password); - final TextInputLayout etPasswordA = (TextInputLayout) promptsView.findViewById(R.id.etWalletPasswordA); - etPasswordA.setHint(getString(R.string.prompt_changepw, walletName)); + } - final TextInputLayout etPasswordB = (TextInputLayout) promptsView.findViewById(R.id.etWalletPasswordB); - etPasswordB.setHint(getString(R.string.prompt_changepwB, walletName)); + @Override + public void onGenerate(final String name, final String password) { + createWallet(name, password, + new WalletCreator() { + public boolean createWallet(File aFile, String password) { + Wallet newWallet = WalletManager.getInstance() + .createWallet(aFile, password, MNEMONIC_LANGUAGE); + boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok); + if (!success) { + Timber.e(newWallet.getErrorString()); + toast(newWallet.getErrorString()); + } + newWallet.close(); + return success; + } + }); + } - LinearLayout llFingerprintAuth = (LinearLayout) promptsView.findViewById(R.id.llFingerprintAuth); - final Switch swFingerprintAllowed = (Switch) llFingerprintAuth.getChildAt(0); - if (FingerprintHelper.isDeviceSupported(getActivity())) { - llFingerprintAuth.setVisibility(View.VISIBLE); + @Override + public void onGenerate(final String name, final String password, final String seed, + final long restoreHeight) { + createWallet(name, password, + new WalletCreator() { + public boolean createWallet(File aFile, String password) { + Wallet newWallet = WalletManager.getInstance(). + recoveryWallet(aFile, password, seed, restoreHeight); + boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok); + if (!success) { + Timber.e(newWallet.getErrorString()); + toast(newWallet.getErrorString()); + } + newWallet.close(); + return success; + } + }); + } - swFingerprintAllowed.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (!swFingerprintAllowed.isChecked()) return; - - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setMessage(Html.fromHtml(getString(R.string.generate_fingerprint_warn))) - .setCancelable(false) - .setPositiveButton(getString(R.string.label_ok), null) - .setNegativeButton(getString(R.string.label_cancel), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - swFingerprintAllowed.setChecked(false); - } - }) - .show(); - } - }); + @Override + public void onGenerate(final String name, final String password, + final String address, final String viewKey, final String spendKey, + final long restoreHeight) { + createWallet(name, password, + new WalletCreator() { + public boolean createWallet(File aFile, String password) { + Wallet newWallet = WalletManager.getInstance() + .createWalletWithKeys(aFile, password, MNEMONIC_LANGUAGE, restoreHeight, + address, viewKey, spendKey); + boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok); + if (!success) { + Timber.e(newWallet.getErrorString()); + toast(newWallet.getErrorString()); + } + newWallet.close(); + return success; + } + }); + } - try { - swFingerprintAllowed.setChecked(FingerprintHelper.isFingerprintAuthAllowed(walletName)); - } catch (KeyStoreException ex) { - ex.printStackTrace(); + void toast(final String msg) { + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(LoginActivity.this, msg, Toast.LENGTH_LONG).show(); } + }); + } + + @Override + public void onAccept(final String name, final String password) { + File walletFolder = getStorageRoot(); + File walletFile = new File(walletFolder, name); + Timber.d("New Wallet %s", walletFile.getAbsolutePath()); + walletFile.delete(); // when recovering wallets, the cache seems corrupt + + boolean rc = 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 { + Timber.e("Wallet store failed to %s", walletFile.getAbsolutePath()); + Toast.makeText(LoginActivity.this, getString(R.string.generate_wallet_create_failed), Toast.LENGTH_LONG).show(); } + } - etPasswordA.getEditText().addTextChangedListener(new TextWatcher() { - @Override - public void afterTextChanged(Editable s) { - if (etPasswordA.getError() != null) { - etPasswordA.setError(null); - } - if (etPasswordB.getError() != null) { - etPasswordB.setError(null); + Wallet.Status testWallet(String path, String password) { + Timber.d("testing wallet %s", 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(); + Timber.d("wallet tested %s", aWallet.getStatus()); + aWallet.close(); + return status; + } + + 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, boolean ignoreCacheError) { + if (walletExists(dstWallet, true) && !overwrite) return false; + boolean success = false; + File srcDir = srcWallet.getParentFile(); + String srcName = srcWallet.getName(); + File dstDir = dstWallet.getParentFile(); + String dstName = dstWallet.getName(); + try { + try { + copyFile(new File(srcDir, srcName), new File(dstDir, dstName)); + } catch (IOException ex) { + Timber.d("CACHE %s", ignoreCacheError); + if (!ignoreCacheError) { // ignore cache backup error if backing up (can be resynced) + throw ex; } } + 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) { + Timber.e("wallet copy failed: %s", ex.getMessage()); + // try to rollback + deleteWallet(dstWallet); + } + return success; + } - @Override - public void beforeTextChanged(CharSequence s, int start, - int count, int after) { - } + // do our best to delete as much as possible of the wallet files + boolean deleteWallet(File walletFile) { + Timber.d("deleteWallet %s", walletFile.getAbsolutePath()); + File dir = walletFile.getParentFile(); + String name = walletFile.getName(); + boolean success = true; + File cacheFile = new File(dir, name); + if (cacheFile.exists()) { + success = cacheFile.delete(); + } + success = new File(dir, name + ".keys").delete() && success; + File addressFile = new File(dir, name + ".address.txt"); + if (addressFile.exists()) { + success = addressFile.delete() && success; + } + Timber.d("deleteWallet is %s", success); + return success; + } - @Override - public void onTextChanged(CharSequence s, int start, - int before, int count) { + 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 { + if (inChannel != null) + inChannel.close(); + if (outChannel != null) + outChannel.close(); + } + } + + @Override + public void onBackPressed() { + Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container); + if (f instanceof GenerateReviewFragment) { + if (((GenerateReviewFragment) f).backOk()) { + super.onBackPressed(); } - }); + } else if (f instanceof LoginFragment) { + if (((LoginFragment) f).isFabOpen()) { + ((LoginFragment) f).animateFAB(); + } else { + super.onBackPressed(); + } + } else { + super.onBackPressed(); + } + } - etPasswordB.getEditText().addTextChangedListener(new TextWatcher() { - @Override - public void afterTextChanged(Editable s) { - if (etPasswordA.getError() != null) { - etPasswordA.setError(null); - } - if (etPasswordB.getError() != null) { - etPasswordB.setError(null); + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_create_help_new: + HelpFragment.display(getSupportFragmentManager(), R.string.help_create_new); + return true; + case R.id.action_create_help_keys: + HelpFragment.display(getSupportFragmentManager(), R.string.help_create_keys); + return true; + case R.id.action_create_help_view: + HelpFragment.display(getSupportFragmentManager(), R.string.help_create_view); + return true; + case R.id.action_create_help_seed: + HelpFragment.display(getSupportFragmentManager(), R.string.help_create_seed); + return true; + case R.id.action_details_help: + HelpFragment.display(getSupportFragmentManager(), R.string.help_details); + return true; + case R.id.action_details_changepw: + onWalletChangePassword(); + return true; + case R.id.action_license_info: + AboutFragment.display(getSupportFragmentManager()); + return true; + case R.id.action_help_list: + HelpFragment.display(getSupportFragmentManager(), R.string.help_list); + return true; + case R.id.action_privacy_policy: + PrivacyFragment.display(getSupportFragmentManager()); + return true; + case R.id.action_testnet: + try { + LoginFragment loginFragment = (LoginFragment) + getSupportFragmentManager().findFragmentById(R.id.fragment_container); + item.setChecked(loginFragment.onTestnetMenuItem()); + } catch (ClassCastException ex) { + // never mind then } - } + return true; + default: + return super.onOptionsItemSelected(item); + } + } - @Override - public void beforeTextChanged(CharSequence s, int start, - int count, int after) { - } + public void setNetworkType(NetworkType networkType) { + WalletManager.getInstance().setNetworkType(networkType); + } - @Override - public void onTextChanged(CharSequence s, int start, - int before, int count) { - } - }); + private class AsyncOpenWallet extends AsyncTask { + final static int OK = 0; + final static int TIMEOUT = 1; + final static int INVALID = 2; + final static int IOEX = 3; - // set dialog message - alertDialogBuilder - .setCancelable(false) - .setPositiveButton(getString(R.string.label_ok), null) - .setNegativeButton(getString(R.string.label_cancel), - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - Helper.hideKeyboardAlways(getActivity()); - dialog.cancel(); - openDialog = null; - } - }); + WalletNode walletNode; - openDialog = alertDialogBuilder.create(); - openDialog.setOnShowListener(new DialogInterface.OnShowListener() { - @Override - public void onShow(final DialogInterface dialog) { - Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE); - button.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - String newPasswordA = etPasswordA.getEditText().getText().toString(); - String newPasswordB = etPasswordB.getEditText().getText().toString(); - // disallow empty passwords - if (newPasswordA.isEmpty()) { - etPasswordA.setError(getString(R.string.generate_empty_passwordB)); - } else if (!newPasswordA.equals(newPasswordB)) { - etPasswordB.setError(getString(R.string.generate_bad_passwordB)); - } else if (newPasswordA.equals(newPasswordB)) { - new AsyncChangePassword().execute(walletName, walletPassword, newPasswordA, Boolean.toString(swFingerprintAllowed.isChecked())); - Helper.hideKeyboardAlways(getActivity()); - openDialog.dismiss(); - openDialog = null; - } - } - }); + @Override + protected void onPreExecute() { + super.onPreExecute(); + showProgressDialog(R.string.open_progress, DAEMON_TIMEOUT / 4); + } + + @Override + protected Integer doInBackground(WalletNode... params) { + if (params.length != 1) return INVALID; + this.walletNode = params[0]; + if (!walletNode.isValid()) return INVALID; + + Timber.d("checking %s", walletNode.getAddress()); + + try { + long timeDA = new Date().getTime(); + SocketAddress address = walletNode.getSocketAddress(); + long timeDB = new Date().getTime(); + Timber.d("Resolving " + walletNode.getAddress() + " took " + (timeDB - timeDA) + "ms."); + Socket socket = new Socket(); + long timeA = new Date().getTime(); + socket.connect(address, LoginActivity.DAEMON_TIMEOUT); + socket.close(); + long timeB = new Date().getTime(); + long time = timeB - timeA; + Timber.d("Daemon " + walletNode.getAddress() + " is " + time + "ms away."); + return (time < LoginActivity.DAEMON_TIMEOUT ? OK : TIMEOUT); + } catch (IOException ex) { + Timber.d("Cannot reach daemon %s because %s", walletNode.getAddress(), ex.getMessage()); + return IOEX; + } catch (IllegalArgumentException ex) { + Timber.d("Cannot reach daemon %s because %s", walletNode.getAddress(), ex.getMessage()); + return INVALID; } - }); + } - // accept keyboard "ok" - etPasswordB.getEditText().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)) { - String newPasswordA = etPasswordA.getEditText().getText().toString(); - String newPasswordB = etPasswordB.getEditText().getText().toString(); - // disallow empty passwords - if (newPasswordA.isEmpty()) { - etPasswordA.setError(getString(R.string.generate_empty_passwordB)); - } else if (!newPasswordA.equals(newPasswordB)) { - etPasswordB.setError(getString(R.string.generate_bad_passwordB)); - } else if (newPasswordA.equals(newPasswordB)) { - new AsyncChangePassword().execute(walletName, walletPassword, newPasswordA, Boolean.toString(swFingerprintAllowed.isChecked())); - Helper.hideKeyboardAlways(getActivity()); - openDialog.dismiss(); - openDialog = null; - } - return true; - } - return false; + @Override + protected void onPostExecute(Integer result) { + super.onPostExecute(result); + if (isDestroyed()) { + return; } - }); - return openDialog; + dismissProgressDialog(); + switch (result) { + case OK: + Timber.d("selected wallet is .%s.", walletNode.getName()); + // now it's getting real, onValidateFields if wallet exists + promptAndStart(walletNode); + break; + case TIMEOUT: + Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_connect_timeout), Toast.LENGTH_LONG).show(); + break; + case INVALID: + Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_node_invalid), Toast.LENGTH_LONG).show(); + break; + case IOEX: + Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_connect_ioex), Toast.LENGTH_LONG).show(); + break; + } + } } -} + void promptAndStart(WalletNode walletNode) { + File walletFile = Helper.getWalletFile(this, walletNode.getName()); + if (WalletManager.getInstance().walletExists(walletFile)) { + WalletManager.getInstance().setDaemon(walletNode); + Helper.promptPassword(LoginActivity.this, walletNode.getName(), false, new Helper.PasswordAction() { + @Override + public void action(String walletName, String password, boolean fingerprintUsed) { + startWallet(walletName, password, fingerprintUsed); + } + }); + } else { // this cannot really happen as we prefilter choices + Toast.makeText(this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java index 5e066742a8..f81d7f2f47 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java @@ -262,7 +262,7 @@ public boolean onContextInteraction(MenuItem item, WalletManager.WalletInfo list /*case R.id.action_backup: activityCallback.onWalletBackup(listItem.name); break;*/ - case R.id.action_backup_file: + case R.id.action_backup: activityCallback.onWalletBackupToFile(listItem.name); break; case R.id.action_backup_nfc: //TODO 备份到NFC From 4b846dc7b38c1d6ec864f1c18892160c2b67aecd Mon Sep 17 00:00:00 2001 From: "jianjunchu@gmail.com" Date: Wed, 9 May 2018 00:27:30 +0800 Subject: [PATCH 05/12] add nfc support --- app/lib/jacksum.jar | Bin 0 -> 199398 bytes app/lib/sunjce_provider.jar | Bin 0 -> 170257 bytes .../xmrwallet/nfc/AuthenticationException.java | 4 ++++ .../java/com/m2049r/xmrwallet/nfc/TagUtil.java | 4 ++++ .../com/m2049r/xmrwallet/nfc/ThreeDES.java | 4 ++++ 5 files changed, 12 insertions(+) create mode 100755 app/lib/jacksum.jar create mode 100755 app/lib/sunjce_provider.jar create mode 100644 app/src/main/java/com/m2049r/xmrwallet/nfc/AuthenticationException.java create mode 100644 app/src/main/java/com/m2049r/xmrwallet/nfc/TagUtil.java create mode 100644 app/src/main/java/com/m2049r/xmrwallet/nfc/ThreeDES.java diff --git a/app/lib/jacksum.jar b/app/lib/jacksum.jar new file mode 100755 index 0000000000000000000000000000000000000000..e71161f8e46ad2782d6ca03d70192e70c2c94ee1 GIT binary patch literal 199398 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≪L=}fY(eb*Y{ zGEOk`JaRhtd{<`eH9a-6DmsQhicj(RTy`wGaF6nMwC8nay+3^dtK3AknJDw|c zIqb?=z>!V39e>UmsFC|sr)-Xh=8>>cHvegK@Dq$ZV+R}5wACKM9FlnkZjzW8+%Yz} zE?{mV7IJ28mcDOJ94~IXAm3*N;Fz9W@{~bhT>M0y9CzN21873w0VF+*KPq8k4%nhB z05uNG9F>uH-6-n~9%}^1-2?~h970X3movDQ6<~cpZ9Ui8I)>$H$gT*b)q3U@el+4G-Fi+U`HEQY7U)lq%(^DV*UQRd0bySt4~GF~X4Syf zzw_4<=?az}o}~dEm_^=Gn)K( zHiCGd?!YGn{I&p{4UW)3(fxQK+b>-MJzEA(J~6?ObW#^sj)zT-*O0@dH^;U{Nmc4ziw3Op4LQ0OrrnsJlC<^@;?tb6C+nETWp-qojosw) z>hd#p8yl0fSf?dO*wzOcB2MAwDT*WRbrr>N)TZdrz)dvq1B%U4Jde1)z;X#bk-!dq-}PjrK;;pYhzlwti6$N)YSAzKqU=h zMPN#3cPD9*|G^X0C(79FsBNkpGu~egCL z`c`qTsOgAr=?j)XS=umwQl|eH`hIZN4EceSt0xs_V?_e4uMaK~TVFn2y3UN{&8$KF z{C!X4j91~HvUHsyRD(ips$H(4^tIAYJ@4@2iLg%#zu?++ESyLIflxK?F#0Dq!)`WM z*L@6IY~k!)mX}RKk7!e;1Jjt&N{V8&!=)KRO7pr(vRlfEbHB3dRdh;tBHGMB1W;G0+(5wB&{-2x;Ag^xxO=Ba5mBmroDYODQIi z)8z3-n6q#QdnF8*E wlZb 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%0A`(RHw$*J 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 z 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 literal 0 HcmV?d00001 diff --git a/app/src/main/java/com/m2049r/xmrwallet/nfc/AuthenticationException.java b/app/src/main/java/com/m2049r/xmrwallet/nfc/AuthenticationException.java new file mode 100644 index 0000000000..6054affb5b --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/nfc/AuthenticationException.java @@ -0,0 +1,4 @@ +package com.m2049r.xmrwallet.nfc; + +public class AuthenticationException { +} diff --git a/app/src/main/java/com/m2049r/xmrwallet/nfc/TagUtil.java b/app/src/main/java/com/m2049r/xmrwallet/nfc/TagUtil.java new file mode 100644 index 0000000000..4a366c4428 --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/nfc/TagUtil.java @@ -0,0 +1,4 @@ +package com.m2049r.xmrwallet.nfc; + +public class TagUtil { +} diff --git a/app/src/main/java/com/m2049r/xmrwallet/nfc/ThreeDES.java b/app/src/main/java/com/m2049r/xmrwallet/nfc/ThreeDES.java new file mode 100644 index 0000000000..604871ef2e --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/nfc/ThreeDES.java @@ -0,0 +1,4 @@ +package com.m2049r.xmrwallet.nfc; + +public class ThreeDES { +} From 086ae995e307c9b1a11d675fee09ccb11d8a8cbb Mon Sep 17 00:00:00 2001 From: "jianjunchu@gmail.com" Date: Wed, 9 May 2018 00:48:33 +0800 Subject: [PATCH 06/12] nfc support --- .idea/misc.xml | 2 +- app/build.gradle | 4 +- app/src/main/AndroidManifest.xml | 1 + .../xmrwallet/GenerateReviewFragment.java | 1326 ++++---------- .../com/m2049r/xmrwallet/LoginActivity.java | 102 ++ .../com/m2049r/xmrwallet/LoginFragment.java | 4 +- .../nfc/AuthenticationException.java | 17 +- .../com/m2049r/xmrwallet/nfc/TagUtil.java | 1594 +++++++++++++++++ .../com/m2049r/xmrwallet/nfc/ThreeDES.java | 69 +- app/src/main/res/values/strings.xml | 4 - 10 files changed, 2177 insertions(+), 946 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index c0f68eddd7..99202cc2d6 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -25,7 +25,7 @@ - + diff --git a/app/build.gradle b/app/build.gradle index 3787f8eff2..ab06ef4357 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -72,7 +72,9 @@ dependencies { implementation "com.jakewharton.timber:timber:$rootProject.ext.timberVersion" implementation 'com.nulab-inc:zxcvbn:1.2.3' - + //implementation 'sunjce_provider:sunjce_provider:1.0' + compile files('lib/sunjce_provider.jar') + compile files('lib/jacksum.jar') testImplementation "junit:junit:$rootProject.ext.junitVersion" testImplementation "org.mockito:mockito-all:$rootProject.ext.mockitoVersion" testImplementation "com.squareup.okhttp3:mockwebserver:$rootProject.ext.okHttpVersion" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4bf5b3a696..aa708d26dc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,6 +8,7 @@ + { - @Override - protected void onPreExecute() { - super.onPreExecute(); - showProgressDialog(R.string.rename_progress); - } - - @Override - protected Boolean doInBackground(String... params) { - if (params.length != 2) return false; - File walletFile = Helper.getWalletFile(LoginActivity.this, params[0]); - String newName = params[1]; - boolean success = renameWallet(walletFile, newName); - try { - if (success && FingerprintHelper.isFingerprintAuthAllowed(params[0])) { - String savedPass = KeyStoreHelper.loadWalletUserPass(LoginActivity.this, params[0]); - KeyStoreHelper.saveWalletUserPass(LoginActivity.this, newName, savedPass); - KeyStoreHelper.removeWalletUserPass(LoginActivity.this, params[0]); - } - } catch (KeyStoreException ex) { - ex.printStackTrace(); - } - return success; - } - - @Override - protected void onPostExecute(Boolean result) { - super.onPostExecute(result); - if (isDestroyed()) { - return; - } - dismissProgressDialog(); - if (result) { - reloadWalletList(); - } else { - Toast.makeText(LoginActivity.this, getString(R.string.rename_failed), Toast.LENGTH_LONG).show(); - } - } + void copyViewKey() { + Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_viewkey), tvWalletViewKey.getText().toString()); + Toast.makeText(getActivity(), getString(R.string.message_copy_viewkey), Toast.LENGTH_SHORT).show(); } - // copy + delete seems safer than rename because we call rollback easily - boolean renameWallet(File walletFile, String newName) { - if (copyWallet(walletFile, new File(walletFile.getParentFile(), newName), false, true)) { - deleteWallet(walletFile); - return true; - } else { - return false; - } + void copyAddress() { + Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_address), tvWalletAddress.getText().toString()); + Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show(); } - @Override - public void onWalletRename(final String walletName) { - Timber.d("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(getString(R.string.label_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(getString(R.string.label_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(); + void nocopy() { + Toast.makeText(getActivity(), getString(R.string.message_nocopy), Toast.LENGTH_SHORT).show(); } - private class AsyncBackup extends AsyncTask { - @Override - protected void onPreExecute() { - super.onPreExecute(); - showProgressDialog(R.string.backup_progress); - } - - @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); - if (isDestroyed()) { - return; - } - dismissProgressDialog(); - if (!result) { - Toast.makeText(LoginActivity.this, getString(R.string.backup_failed), Toast.LENGTH_LONG).show(); + void showAdvancedInfo() { + llAdvancedInfo.setVisibility(View.VISIBLE); + bAdvancedInfo.setVisibility(View.GONE); + scrollview.post(new Runnable() { + @Override + public void run() { + scrollview.fullScroll(ScrollView.FOCUS_DOWN); } - } + }); } - private boolean backupWallet(String walletName) { - File backupFolder = new File(getStorageRoot(), "backups"); - if (!backupFolder.exists()) { - if (!backupFolder.mkdir()) { - Timber.e("Cannot create backup dir %s", backupFolder.getAbsolutePath()); - return false; - } - // make folder visible over USB/MTP - MediaScannerConnection.scanFile(this, new String[]{backupFolder.toString()}, null, null); - } - File walletFile = Helper.getWalletFile(LoginActivity.this, walletName); - File backupFile = new File(backupFolder, walletName); - Timber.d("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 - boolean success = copyWallet(walletFile, backupFile, true, true); - Timber.d("copyWallet is %s", success); - return success; - } + String type; - @Override - public void onWalletBackup(String walletName) { - Timber.d("backup for wallet ." + walletName + "."); - new AsyncBackup().execute(walletName); + private void acceptWallet() { + bAccept.setEnabled(false); + acceptCallback.onAccept(walletName, walletPassword); } - private class AsyncArchive extends AsyncTask { - @Override - protected void onPreExecute() { - super.onPreExecute(); - showProgressDialog(R.string.archive_progress); - } + private class AsyncShow extends AsyncTask { + String name; + String address; + String seed; + String viewKey; + String spendKey; + boolean isWatchOnly; + Wallet.Status status; @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))) { - KeyStoreHelper.removeWalletUserPass(LoginActivity.this, walletName); - return true; + String walletPath = params[0]; + + Wallet wallet; + boolean closeWallet; + if (type.equals(GenerateReviewFragment.VIEW_TYPE_WALLET)) { + wallet = GenerateReviewFragment.this.walletCallback.getWallet(); + closeWallet = false; } else { + wallet = WalletManager.getInstance().openWallet(walletPath, walletPassword); + closeWallet = true; + } + name = wallet.getName(); + status = wallet.getStatus(); + if (status != Wallet.Status.Status_Ok) { + Timber.e(wallet.getErrorString()); + if (closeWallet) wallet.close(); return false; } + + address = wallet.getAddress(); + seed = wallet.getSeed(); + viewKey = wallet.getSecretViewKey(); + spendKey = isWatchOnly ? getActivity().getString(R.string.label_watchonly) : wallet.getSecretSpendKey(); + isWatchOnly = wallet.isWatchOnly(); + if (closeWallet) wallet.close(); + return true; } @Override protected void onPostExecute(Boolean result) { super.onPostExecute(result); - if (isDestroyed()) { - return; - } - dismissProgressDialog(); + if (!isAdded()) return; // never mind + walletName = name; if (result) { - reloadWalletList(); + if (type.equals(GenerateReviewFragment.VIEW_TYPE_ACCEPT)) { + bAccept.setVisibility(View.VISIBLE); + bAccept.setEnabled(true); + } + if (walletPassword != null) { + llPassword.setVisibility(View.VISIBLE); + tvWalletPassword.setText(walletPassword); + } + tvWalletAddress.setText(address); + tvWalletMnemonic.setText(seed); + tvWalletViewKey.setText(viewKey); + tvWalletSpendKey.setText(spendKey); + bAdvancedInfo.setVisibility(View.VISIBLE); + bCopyAddress.setClickable(true); + bCopyAddress.setImageResource(R.drawable.ic_content_copy_black_24dp); + activityCallback.setTitle(name, getString(R.string.details_title)); + activityCallback.setToolbarButton( + GenerateReviewFragment.VIEW_TYPE_ACCEPT.equals(type) ? Toolbar.BUTTON_NONE : Toolbar.BUTTON_BACK); } else { - Toast.makeText(LoginActivity.this, getString(R.string.archive_failed), Toast.LENGTH_LONG).show(); + // TODO show proper error message and/or end the fragment? + tvWalletAddress.setText(status.toString()); + tvWalletMnemonic.setText(status.toString()); + tvWalletViewKey.setText(status.toString()); + tvWalletSpendKey.setText(status.toString()); } + hideProgress(); } } - @Override - public void onWalletArchive(final String walletName) { - Timber.d("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(); - } + Listener activityCallback = null; + ProgressListener progressCallback = null; + AcceptListener acceptCallback = null; + ListenerWithWallet walletCallback = null; - void reloadWalletList() { - Timber.d("reloadWalletList()"); - try { - LoginFragment loginFragment = (LoginFragment) - getSupportFragmentManager().findFragmentById(R.id.fragment_container); - if (loginFragment != null) { - loginFragment.loadList(); - } - } catch (ClassCastException ex) { - } - } - - public void onWalletChangePassword() {//final String walletName, final String walletPassword) { - try { - GenerateReviewFragment detailsFragment = (GenerateReviewFragment) - getSupportFragmentManager().findFragmentById(R.id.fragment_container); - AlertDialog dialog = detailsFragment.createChangePasswordDialog(); - if (dialog != null) { - Helper.showKeyboard(dialog); - dialog.show(); - } - } catch (ClassCastException ex) { - Timber.w("onWalletChangePassword() called, but no GenerateReviewFragment active"); - } - } + public interface Listener { + void setTitle(String title, String subtitle); - @Override - public void onAddWallet(String type) { - if (checkServiceRunning()) return; - startGenerateFragment(type); + void setToolbarButton(int type); } - //////////////////////////////////////// - // LoginFragment.Listener - //////////////////////////////////////// - @Override - public SharedPreferences getPrefs() { - return getPreferences(Context.MODE_PRIVATE); - } + public interface ProgressListener { + void showProgressDialog(int msgId); - @Override - public File getStorageRoot() { - return Helper.getWalletRoot(getApplicationContext()); + void dismissProgressDialog(); } - //////////////////////////////////////// - //////////////////////////////////////// - @Override - public void showNet() { - switch (WalletManager.getInstance().getNetworkType()) { - case NetworkType_Mainnet: - toolbar.setSubtitle(getString(R.string.connect_mainnet)); - toolbar.setBackgroundResource(R.drawable.backgound_toolbar_mainnet); - break; - case NetworkType_Testnet: - toolbar.setSubtitle(getString(R.string.connect_testnet)); - toolbar.setBackgroundResource(R.color.colorPrimaryDark); - break; - case NetworkType_Stagenet: - toolbar.setSubtitle(getString(R.string.connect_stagenet)); - toolbar.setBackgroundResource(R.color.colorPrimaryDark); - break; - default: - throw new IllegalStateException("NetworkType unknown: " + WalletManager.getInstance().getNetworkType()); - } + public interface AcceptListener { + void onAccept(String name, String password); } - @Override - protected void onPause() { - Timber.d("onPause()"); - super.onPause(); + public interface ListenerWithWallet { + Wallet getWallet(); } - ProgressDialog progressDialog = null; - @Override - public void showProgressDialog(int msgId) { - showProgressDialog(msgId, 0); - } - - private void showProgressDialog(int msgId, long delay) { - dismissProgressDialog(); // just in case - progressDialog = new MyProgressDialog(LoginActivity.this, msgId); - if (delay > 0) { - Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - public void run() { - if (progressDialog != null) progressDialog.show(); - } - }, delay); - } else { - progressDialog.show(); + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof Listener) { + this.activityCallback = (Listener) context; } - } - - @Override - public void dismissProgressDialog() { - if (progressDialog != null && progressDialog.isShowing()) { - progressDialog.dismiss(); + if (context instanceof ProgressListener) { + this.progressCallback = (ProgressListener) context; + } + if (context instanceof AcceptListener) { + this.acceptCallback = (AcceptListener) context; + } + if (context instanceof ListenerWithWallet) { + this.walletCallback = (ListenerWithWallet) context; } - progressDialog = null; - } - - @Override - protected void onDestroy() { - dismissProgressDialog(); - super.onDestroy(); } @Override - protected void onResume() { + public void onResume() { super.onResume(); Timber.d("onResume()"); - // wait for WalletService to finish - if (WalletService.Running && (progressDialog == null)) { - // and show a progress dialog, but only if there isn't one already - new AsyncWaitForService().execute(); - } + activityCallback.setTitle(walletName, getString(R.string.details_title)); + activityCallback.setToolbarButton( + GenerateReviewFragment.VIEW_TYPE_ACCEPT.equals(type) ? Toolbar.BUTTON_NONE : Toolbar.BUTTON_BACK); } - private class MyProgressDialog extends ProgressDialog { - Activity activity; - - MyProgressDialog(Activity activity, int msgId) { - super(activity); - this.activity = activity; - setCancelable(false); - setMessage(activity.getString(msgId)); - } - - @Override - public void onBackPressed() { - // prevent back button - } - } - - - private class AsyncWaitForService extends AsyncTask { - @Override - protected void onPreExecute() { - super.onPreExecute(); - showProgressDialog(R.string.service_progress); - } - - @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); - if (isDestroyed()) { - return; - } - dismissProgressDialog(); - } - } - - void startWallet(String walletName, String walletPassword, boolean fingerprintUsed) { - Timber.d("startWallet()"); - Intent intent = new Intent(getApplicationContext(), WalletActivity.class); - intent.putExtra(WalletActivity.REQUEST_ID, walletName); - intent.putExtra(WalletActivity.REQUEST_PW, walletPassword); - intent.putExtra(WalletActivity.REQUEST_FINGERPRINT_USED, fingerprintUsed); - startActivity(intent); + public void showProgress() { + pbProgress.setVisibility(View.VISIBLE); } - void startDetails(File walletFile, String password, String type) { - Timber.d("startDetails()"); - Bundle b = new Bundle(); - b.putString("path", walletFile.getAbsolutePath()); - b.putString("password", password); - b.putString("type", type); - startReviewFragment(b); + public void hideProgress() { + pbProgress.setVisibility(View.GONE); } - void startReceive(File walletFile, String password) { - Timber.d("startReceive()"); - Bundle b = new Bundle(); - b.putString("path", walletFile.getAbsolutePath()); - b.putString("password", password); - startReceiveFragment(b); + boolean backOk() { + return !type.equals(GenerateReviewFragment.VIEW_TYPE_ACCEPT); } @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { - Timber.d("onRequestPermissionsResult()"); - switch (requestCode) { - case Helper.PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE: - // If request is cancelled, the result arrays are empty. - if (grantResults.length > 0 - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - startLoginFragment = true; - } else { - String msg = getString(R.string.message_strorage_not_permitted); - Timber.e(msg); - Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); - } - break; - default: - } + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); } - private boolean startLoginFragment = false; - @Override - protected void onResumeFragments() { - super.onResumeFragments(); - if (startLoginFragment) { - startLoginFragment(); - startLoginFragment = false; + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + String type = getArguments().getString("type"); + if (GenerateReviewFragment.VIEW_TYPE_ACCEPT.equals(type)) { + inflater.inflate(R.menu.wallet_details_help_menu, menu); + super.onCreateOptionsMenu(menu, inflater); + } else { + inflater.inflate(R.menu.wallet_details_menu, menu); + super.onCreateOptionsMenu(menu, inflater); } } - void startLoginFragment() { - // we set these here because we cannot be ceratin we have permissions for storage before - Helper.setMoneroHome(this); - Helper.initLogger(this); - Fragment fragment = new LoginFragment(); - getSupportFragmentManager().beginTransaction() - .add(R.id.fragment_container, fragment).commit(); - Timber.d("LoginFragment added"); - } - - void startGenerateFragment(String type) { - Bundle extras = new Bundle(); - extras.putString(GenerateFragment.TYPE, type); - replaceFragment(new GenerateFragment(), GENERATE_STACK, extras); - Timber.d("GenerateFragment placed"); - } - - void startReviewFragment(Bundle extras) { - replaceFragment(new GenerateReviewFragment(), null, extras); - Timber.d("GenerateReviewFragment placed"); - } - - void startReceiveFragment(Bundle extras) { - replaceFragment(new ReceiveFragment(), null, extras); - Timber.d("ReceiveFragment placed"); - } - - void replaceFragment(Fragment newFragment, String stackName, Bundle extras) { - if (extras != null) { - newFragment.setArguments(extras); + boolean changeWalletPassword(String newPassword) { + Wallet wallet; + boolean closeWallet; + if (type.equals(GenerateReviewFragment.VIEW_TYPE_WALLET)) { + wallet = GenerateReviewFragment.this.walletCallback.getWallet(); + closeWallet = false; + } else { + wallet = WalletManager.getInstance().openWallet(walletPath, walletPassword); + closeWallet = true; } - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.fragment_container, newFragment); - transaction.addToBackStack(stackName); - transaction.commit(); - } - void popFragmentStack(String name) { - getSupportFragmentManager().popBackStack(name, FragmentManager.POP_BACK_STACK_INCLUSIVE); + boolean ok = false; + if (wallet.getStatus() == Wallet.Status.Status_Ok) { + wallet.setPassword(newPassword); + wallet.store(); + ok = true; + } else { + Timber.e(wallet.getErrorString()); + } + if (closeWallet) wallet.close(); + return ok; } - ////////////////////////////////////////// - // GenerateFragment.Listener - ////////////////////////////////////////// - static final String MNEMONIC_LANGUAGE = "English"; // see mnemonics/electrum-words.cpp for more - - private class AsyncCreateWallet extends AsyncTask { - final String walletName; - final String walletPassword; - final WalletCreator walletCreator; - - File newWalletFile; - - AsyncCreateWallet(final String name, final String password, - final WalletCreator walletCreator) { - super(); - this.walletName = name; - this.walletPassword = password; - this.walletCreator = walletCreator; - } + private class AsyncChangePassword extends AsyncTask { + String newPassword; @Override protected void onPreExecute() { super.onPreExecute(); - showProgressDialog(R.string.generate_wallet_creating); + if (progressCallback != null) + progressCallback.showProgressDialog(R.string.changepw_progress); } @Override - protected Boolean doInBackground(Void... params) { - // check if the wallet we want to create already exists - File walletFolder = getStorageRoot(); - if (!walletFolder.isDirectory()) { - Timber.e("Wallet dir " + walletFolder.getAbsolutePath() + "is not a directory"); - return false; - } - File cacheFile = new File(walletFolder, walletName); - File keysFile = new File(walletFolder, walletName + ".keys"); - File addressFile = new File(walletFolder, walletName + ".address.txt"); - - if (cacheFile.exists() || keysFile.exists() || addressFile.exists()) { - Timber.e("Some wallet files already exist for %s", cacheFile.getAbsolutePath()); - return false; - } - - newWalletFile = new File(walletFolder, walletName); - boolean success = walletCreator.createWallet(newWalletFile, walletPassword); + protected Boolean doInBackground(String... params) { + if (params.length != 4) return false; + File walletFile = Helper.getWalletFile(getActivity(), params[0]); + String oldPassword = params[1]; + String userPassword = params[2]; + boolean fingerprintAuthAllowed = Boolean.valueOf(params[3]); + newPassword = KeyStoreHelper.getCrazyPass(getActivity(), userPassword); + boolean success = changeWalletPassword(newPassword); if (success) { - return true; - } else { - Timber.e("Could not create new wallet in %s", newWalletFile.getAbsolutePath()); - return false; + if (fingerprintAuthAllowed) { + KeyStoreHelper.saveWalletUserPass(getActivity(), walletName, userPassword); + } else { + KeyStoreHelper.removeWalletUserPass(getActivity(), walletName); + } } + return success; } @Override protected void onPostExecute(Boolean result) { super.onPostExecute(result); - if (isDestroyed()) { + if (getActivity().isDestroyed()) { return; } - dismissProgressDialog(); + if (progressCallback != null) + progressCallback.dismissProgressDialog(); if (result) { - startDetails(newWalletFile, walletPassword, GenerateReviewFragment.VIEW_TYPE_ACCEPT); + Toast.makeText(getActivity(), getString(R.string.changepw_success), Toast.LENGTH_SHORT).show(); + showDetails(newPassword); } else { - walletGenerateError(); + Toast.makeText(getActivity(), getString(R.string.changepw_failed), Toast.LENGTH_LONG).show(); } } } - public void createWallet(final String name, final String password, - final WalletCreator walletCreator) { - new AsyncCreateWallet(name, password, walletCreator) - .executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR); - } + AlertDialog openDialog = null; // for preventing opening of multiple dialogs - void walletGenerateError() { - try { - GenerateFragment genFragment = (GenerateFragment) - getSupportFragmentManager().findFragmentById(R.id.fragment_container); - genFragment.walletGenerateError(); - } catch (ClassCastException ex) { - Timber.e("walletGenerateError() but not in GenerateFragment"); - } - } + public AlertDialog createChangePasswordDialog() { + if (openDialog != null) return null; // we are already open + LayoutInflater li = LayoutInflater.from(getActivity()); + View promptsView = li.inflate(R.layout.prompt_changepw, null); - interface WalletCreator { - boolean createWallet(File aFile, String password); + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity()); + alertDialogBuilder.setView(promptsView); - } + final TextInputLayout etPasswordA = (TextInputLayout) promptsView.findViewById(R.id.etWalletPasswordA); + etPasswordA.setHint(getString(R.string.prompt_changepw, walletName)); - @Override - public void onGenerate(final String name, final String password) { - createWallet(name, password, - new WalletCreator() { - public boolean createWallet(File aFile, String password) { - Wallet newWallet = WalletManager.getInstance() - .createWallet(aFile, password, MNEMONIC_LANGUAGE); - boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok); - if (!success) { - Timber.e(newWallet.getErrorString()); - toast(newWallet.getErrorString()); - } - newWallet.close(); - return success; - } - }); - } + final TextInputLayout etPasswordB = (TextInputLayout) promptsView.findViewById(R.id.etWalletPasswordB); + etPasswordB.setHint(getString(R.string.prompt_changepwB, walletName)); - @Override - public void onGenerate(final String name, final String password, final String seed, - final long restoreHeight) { - createWallet(name, password, - new WalletCreator() { - public boolean createWallet(File aFile, String password) { - Wallet newWallet = WalletManager.getInstance(). - recoveryWallet(aFile, password, seed, restoreHeight); - boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok); - if (!success) { - Timber.e(newWallet.getErrorString()); - toast(newWallet.getErrorString()); - } - newWallet.close(); - return success; - } - }); - } + LinearLayout llFingerprintAuth = (LinearLayout) promptsView.findViewById(R.id.llFingerprintAuth); + final Switch swFingerprintAllowed = (Switch) llFingerprintAuth.getChildAt(0); + if (FingerprintHelper.isDeviceSupported(getActivity())) { + llFingerprintAuth.setVisibility(View.VISIBLE); - @Override - public void onGenerate(final String name, final String password, - final String address, final String viewKey, final String spendKey, - final long restoreHeight) { - createWallet(name, password, - new WalletCreator() { - public boolean createWallet(File aFile, String password) { - Wallet newWallet = WalletManager.getInstance() - .createWalletWithKeys(aFile, password, MNEMONIC_LANGUAGE, restoreHeight, - address, viewKey, spendKey); - boolean success = (newWallet.getStatus() == Wallet.Status.Status_Ok); - if (!success) { - Timber.e(newWallet.getErrorString()); - toast(newWallet.getErrorString()); - } - newWallet.close(); - return success; - } - }); - } + swFingerprintAllowed.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (!swFingerprintAllowed.isChecked()) return; + + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setMessage(Html.fromHtml(getString(R.string.generate_fingerprint_warn))) + .setCancelable(false) + .setPositiveButton(getString(R.string.label_ok), null) + .setNegativeButton(getString(R.string.label_cancel), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + swFingerprintAllowed.setChecked(false); + } + }) + .show(); + } + }); - void toast(final String msg) { - runOnUiThread(new Runnable() { - @Override - public void run() { - Toast.makeText(LoginActivity.this, msg, Toast.LENGTH_LONG).show(); + try { + swFingerprintAllowed.setChecked(FingerprintHelper.isFingerprintAuthAllowed(walletName)); + } catch (KeyStoreException ex) { + ex.printStackTrace(); } - }); - } - - @Override - public void onAccept(final String name, final String password) { - File walletFolder = getStorageRoot(); - File walletFile = new File(walletFolder, name); - Timber.d("New Wallet %s", walletFile.getAbsolutePath()); - walletFile.delete(); // when recovering wallets, the cache seems corrupt - - boolean rc = 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 { - Timber.e("Wallet store failed to %s", walletFile.getAbsolutePath()); - Toast.makeText(LoginActivity.this, getString(R.string.generate_wallet_create_failed), Toast.LENGTH_LONG).show(); - } - } - - Wallet.Status testWallet(String path, String password) { - Timber.d("testing wallet %s", 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(); - Timber.d("wallet tested %s", aWallet.getStatus()); - aWallet.close(); - return status; - } - - 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, boolean ignoreCacheError) { - if (walletExists(dstWallet, true) && !overwrite) return false; - boolean success = false; - File srcDir = srcWallet.getParentFile(); - String srcName = srcWallet.getName(); - File dstDir = dstWallet.getParentFile(); - String dstName = dstWallet.getName(); - try { - try { - copyFile(new File(srcDir, srcName), new File(dstDir, dstName)); - } catch (IOException ex) { - Timber.d("CACHE %s", ignoreCacheError); - if (!ignoreCacheError) { // ignore cache backup error if backing up (can be resynced) - throw ex; + etPasswordA.getEditText().addTextChangedListener(new TextWatcher() { + @Override + public void afterTextChanged(Editable s) { + if (etPasswordA.getError() != null) { + etPasswordA.setError(null); + } + if (etPasswordB.getError() != null) { + etPasswordB.setError(null); } } - 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) { - Timber.e("wallet copy failed: %s", ex.getMessage()); - // try to rollback - deleteWallet(dstWallet); - } - return success; - } - - // do our best to delete as much as possible of the wallet files - boolean deleteWallet(File walletFile) { - Timber.d("deleteWallet %s", walletFile.getAbsolutePath()); - File dir = walletFile.getParentFile(); - String name = walletFile.getName(); - boolean success = true; - File cacheFile = new File(dir, name); - if (cacheFile.exists()) { - success = cacheFile.delete(); - } - success = new File(dir, name + ".keys").delete() && success; - File addressFile = new File(dir, name + ".address.txt"); - if (addressFile.exists()) { - success = addressFile.delete() && success; - } - Timber.d("deleteWallet is %s", 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 { - if (inChannel != null) - inChannel.close(); - if (outChannel != null) - outChannel.close(); - } - } - @Override - public void onBackPressed() { - Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container); - if (f instanceof GenerateReviewFragment) { - if (((GenerateReviewFragment) f).backOk()) { - super.onBackPressed(); + @Override + public void beforeTextChanged(CharSequence s, int start, + int count, int after) { } - } else if (f instanceof LoginFragment) { - if (((LoginFragment) f).isFabOpen()) { - ((LoginFragment) f).animateFAB(); - } else { - super.onBackPressed(); + + @Override + public void onTextChanged(CharSequence s, int start, + int before, int count) { } - } else { - super.onBackPressed(); - } - } + }); - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_create_help_new: - HelpFragment.display(getSupportFragmentManager(), R.string.help_create_new); - return true; - case R.id.action_create_help_keys: - HelpFragment.display(getSupportFragmentManager(), R.string.help_create_keys); - return true; - case R.id.action_create_help_view: - HelpFragment.display(getSupportFragmentManager(), R.string.help_create_view); - return true; - case R.id.action_create_help_seed: - HelpFragment.display(getSupportFragmentManager(), R.string.help_create_seed); - return true; - case R.id.action_details_help: - HelpFragment.display(getSupportFragmentManager(), R.string.help_details); - return true; - case R.id.action_details_changepw: - onWalletChangePassword(); - return true; - case R.id.action_license_info: - AboutFragment.display(getSupportFragmentManager()); - return true; - case R.id.action_help_list: - HelpFragment.display(getSupportFragmentManager(), R.string.help_list); - return true; - case R.id.action_privacy_policy: - PrivacyFragment.display(getSupportFragmentManager()); - return true; - case R.id.action_testnet: - try { - LoginFragment loginFragment = (LoginFragment) - getSupportFragmentManager().findFragmentById(R.id.fragment_container); - item.setChecked(loginFragment.onTestnetMenuItem()); - } catch (ClassCastException ex) { - // never mind then + etPasswordB.getEditText().addTextChangedListener(new TextWatcher() { + @Override + public void afterTextChanged(Editable s) { + if (etPasswordA.getError() != null) { + etPasswordA.setError(null); } - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - public void setNetworkType(NetworkType networkType) { - WalletManager.getInstance().setNetworkType(networkType); - } - - private class AsyncOpenWallet extends AsyncTask { - final static int OK = 0; - final static int TIMEOUT = 1; - final static int INVALID = 2; - final static int IOEX = 3; - - WalletNode walletNode; + if (etPasswordB.getError() != null) { + etPasswordB.setError(null); + } + } - @Override - protected void onPreExecute() { - super.onPreExecute(); - showProgressDialog(R.string.open_progress, DAEMON_TIMEOUT / 4); - } + @Override + public void beforeTextChanged(CharSequence s, int start, + int count, int after) { + } - @Override - protected Integer doInBackground(WalletNode... params) { - if (params.length != 1) return INVALID; - this.walletNode = params[0]; - if (!walletNode.isValid()) return INVALID; + @Override + public void onTextChanged(CharSequence s, int start, + int before, int count) { + } + }); - Timber.d("checking %s", walletNode.getAddress()); + // set dialog message + alertDialogBuilder + .setCancelable(false) + .setPositiveButton(getString(R.string.label_ok), null) + .setNegativeButton(getString(R.string.label_cancel), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + Helper.hideKeyboardAlways(getActivity()); + dialog.cancel(); + openDialog = null; + } + }); - try { - long timeDA = new Date().getTime(); - SocketAddress address = walletNode.getSocketAddress(); - long timeDB = new Date().getTime(); - Timber.d("Resolving " + walletNode.getAddress() + " took " + (timeDB - timeDA) + "ms."); - Socket socket = new Socket(); - long timeA = new Date().getTime(); - socket.connect(address, LoginActivity.DAEMON_TIMEOUT); - socket.close(); - long timeB = new Date().getTime(); - long time = timeB - timeA; - Timber.d("Daemon " + walletNode.getAddress() + " is " + time + "ms away."); - return (time < LoginActivity.DAEMON_TIMEOUT ? OK : TIMEOUT); - } catch (IOException ex) { - Timber.d("Cannot reach daemon %s because %s", walletNode.getAddress(), ex.getMessage()); - return IOEX; - } catch (IllegalArgumentException ex) { - Timber.d("Cannot reach daemon %s because %s", walletNode.getAddress(), ex.getMessage()); - return INVALID; + openDialog = alertDialogBuilder.create(); + openDialog.setOnShowListener(new DialogInterface.OnShowListener() { + @Override + public void onShow(final DialogInterface dialog) { + Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE); + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + String newPasswordA = etPasswordA.getEditText().getText().toString(); + String newPasswordB = etPasswordB.getEditText().getText().toString(); + // disallow empty passwords + if (newPasswordA.isEmpty()) { + etPasswordA.setError(getString(R.string.generate_empty_passwordB)); + } else if (!newPasswordA.equals(newPasswordB)) { + etPasswordB.setError(getString(R.string.generate_bad_passwordB)); + } else if (newPasswordA.equals(newPasswordB)) { + new AsyncChangePassword().execute(walletName, walletPassword, newPasswordA, Boolean.toString(swFingerprintAllowed.isChecked())); + Helper.hideKeyboardAlways(getActivity()); + openDialog.dismiss(); + openDialog = null; + } + } + }); } - } + }); - @Override - protected void onPostExecute(Integer result) { - super.onPostExecute(result); - if (isDestroyed()) { - return; - } - dismissProgressDialog(); - switch (result) { - case OK: - Timber.d("selected wallet is .%s.", walletNode.getName()); - // now it's getting real, onValidateFields if wallet exists - promptAndStart(walletNode); - break; - case TIMEOUT: - Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_connect_timeout), Toast.LENGTH_LONG).show(); - break; - case INVALID: - Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_node_invalid), Toast.LENGTH_LONG).show(); - break; - case IOEX: - Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_connect_ioex), Toast.LENGTH_LONG).show(); - break; + // accept keyboard "ok" + etPasswordB.getEditText().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)) { + String newPasswordA = etPasswordA.getEditText().getText().toString(); + String newPasswordB = etPasswordB.getEditText().getText().toString(); + // disallow empty passwords + if (newPasswordA.isEmpty()) { + etPasswordA.setError(getString(R.string.generate_empty_passwordB)); + } else if (!newPasswordA.equals(newPasswordB)) { + etPasswordB.setError(getString(R.string.generate_bad_passwordB)); + } else if (newPasswordA.equals(newPasswordB)) { + new AsyncChangePassword().execute(walletName, walletPassword, newPasswordA, Boolean.toString(swFingerprintAllowed.isChecked())); + Helper.hideKeyboardAlways(getActivity()); + openDialog.dismiss(); + openDialog = null; + } + return true; + } + return false; } - } + }); + return openDialog; } - void promptAndStart(WalletNode walletNode) { - File walletFile = Helper.getWalletFile(this, walletNode.getName()); - if (WalletManager.getInstance().walletExists(walletFile)) { - WalletManager.getInstance().setDaemon(walletNode); - Helper.promptPassword(LoginActivity.this, walletNode.getName(), false, new Helper.PasswordAction() { - @Override - public void action(String walletName, String password, boolean fingerprintUsed) { - startWallet(walletName, password, fingerprintUsed); - } - }); - } else { // this cannot really happen as we prefilter choices - Toast.makeText(this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show(); - } - } } \ No newline at end of file diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java index 8c3c2ebe92..782afba6e2 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java @@ -49,6 +49,9 @@ import com.m2049r.xmrwallet.model.NetworkType; import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.model.WalletManager; +import com.m2049r.xmrwallet.nfc.AuthenticationException; +import com.m2049r.xmrwallet.nfc.TagUtil; +import com.m2049r.xmrwallet.nfc.ThreeDES; import com.m2049r.xmrwallet.service.WalletService; import com.m2049r.xmrwallet.util.FingerprintHelper; import com.m2049r.xmrwallet.util.Helper; @@ -58,6 +61,7 @@ import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.net.Socket; @@ -349,6 +353,32 @@ protected void onPostExecute(Boolean result) { } } + private class AsyncBackupToNFC extends AsyncTask { + @Override + protected void onPreExecute() { + super.onPreExecute(); + showProgressDialog(R.string.backup_progress); + } + + @Override + protected Boolean doInBackground(String... params) { + if (params.length != 1) return false; + return backupWalletToNFC(params[0]); + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + if (isDestroyed()) { + return; + } + dismissProgressDialog(); + if (!result) { + Toast.makeText(LoginActivity.this, getString(R.string.backup_failed), Toast.LENGTH_LONG).show(); + } + } + } + private boolean backupWallet(String walletName) { File backupFolder = new File(getStorageRoot(), "backups"); if (!backupFolder.exists()) { @@ -370,12 +400,84 @@ private boolean backupWallet(String walletName) { return success; } + private boolean backupWalletToNFC(String walletName) { + boolean success=false; + File walletFile = Helper.getWalletFile(LoginActivity.this, walletName); + //Timber.d("backup " + walletFile.getAbsolutePath() + " to " + backupFile.getAbsolutePath()); + Intent intent = null; + TagUtil tagUtil = null; + boolean authenticated=false; + try { + tagUtil = TagUtil.selectTag(intent, false); + authenticated = tagUtil.authentication(intent, getKey(), false); + }catch (Exception e) { + e.printStackTrace(); + } + + if(authenticated) + { + Toast toast = Toast.makeText(LoginActivity.this, "Authentication Successful", Toast.LENGTH_LONG); + try { + + byte[] bytes = new byte[(int)walletFile.length()]; + new FileInputStream(walletFile).read(bytes); + tagUtil.writeTag(intent,(byte)4,bytes,false); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (AuthenticationException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + } + else + return false; + return success; + } + private byte[] getKey() + { + return ThreeDES.secretKey; + } + + private class AsyncBackupT extends AsyncTask { + @Override + protected void onPreExecute() { + super.onPreExecute(); + showProgressDialog(R.string.backup_progress); + } + + @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); + if (isDestroyed()) { + return; + } + dismissProgressDialog(); + if (!result) { + Toast.makeText(LoginActivity.this, getString(R.string.backup_failed), Toast.LENGTH_LONG).show(); + } + } + } @Override public void onWalletBackupToFile(String walletName) { Timber.d("backup for wallet ." + walletName + "."); new AsyncBackup().execute(walletName); } + @Override + public void onWalletBackupToNFC(String walletName) { + Timber.d("backup to NFC hard wallet for wallet ." + walletName + "."); + new AsyncBackupToNFC().execute(walletName); + } + private class AsyncArchive extends AsyncTask { @Override protected void onPreExecute() { diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java index f81d7f2f47..4dff440174 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java @@ -262,10 +262,10 @@ public boolean onContextInteraction(MenuItem item, WalletManager.WalletInfo list /*case R.id.action_backup: activityCallback.onWalletBackup(listItem.name); break;*/ - case R.id.action_backup: + case R.id.action_backup_file: activityCallback.onWalletBackupToFile(listItem.name); break; - case R.id.action_backup_nfc: //TODO 备份到NFC + case R.id.action_backup_nfc: //backup to NFC activityCallback.onWalletBackupToNFC(listItem.name); break; case R.id.action_archive: diff --git a/app/src/main/java/com/m2049r/xmrwallet/nfc/AuthenticationException.java b/app/src/main/java/com/m2049r/xmrwallet/nfc/AuthenticationException.java index 6054affb5b..a003a65eba 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/nfc/AuthenticationException.java +++ b/app/src/main/java/com/m2049r/xmrwallet/nfc/AuthenticationException.java @@ -1,4 +1,19 @@ package com.m2049r.xmrwallet.nfc; -public class AuthenticationException { +public class AuthenticationException extends Exception{ + + /** + * + */ + private static final long serialVersionUID = 101001L; + + public AuthenticationException(String info) + { + super(info); + } + + public AuthenticationException(String info,Exception e) + { + super(info,e); + } } diff --git a/app/src/main/java/com/m2049r/xmrwallet/nfc/TagUtil.java b/app/src/main/java/com/m2049r/xmrwallet/nfc/TagUtil.java index 4a366c4428..42778effc7 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/nfc/TagUtil.java +++ b/app/src/main/java/com/m2049r/xmrwallet/nfc/TagUtil.java @@ -1,4 +1,1598 @@ package com.m2049r.xmrwallet.nfc; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Date; + +import jonelo.jacksum.JacksumAPI; +import jonelo.jacksum.algorithm.AbstractChecksum; +import jonelo.jacksum.ui.ExitStatus; +import jonelo.sugar.util.ExitException; +import android.content.Intent; +import android.nfc.NfcAdapter; +import android.nfc.Tag; +import android.nfc.tech.MifareClassic; +import android.nfc.tech.MifareUltralight; +import android.nfc.tech.NfcA; +import android.util.Log; + + +/** + * + * @author jianjunchu + * + */ public class TagUtil { + + private static final int TAGUTIL_TYPE_ULTRALIGHT = 1; + private static final int TAGUTIL_TYPE_CLASSIC = 2; + private static final int TAGUTIL_NfcA = 3; + private static final byte PAGE_ADDR_AUTH0 = 42; + private static final byte PAGE_ADDR_AUTH1 = 43; + + // private static android.nfc.Tag tag; + + private static String uid; + private static String finalPage; + + private int tagType; + + private byte[] secretKey; + private byte[] ivDefault = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };// default iv + private byte[] random = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };// radmon number + public byte[] getRandom() { + return random; + } + + public void setRandom(byte[] random) { + this.random = random; + } + + private boolean authorised = true; + private String ERR_MSG; + + private static NfcA nfcA=null; + + public TagUtil(String u,int type) + { + uid=u; + tagType = type; + } + + /** + * get the TagUtil obj, throw exception for not supported nfc tag. + * @param intent + * @param isCheckSUM: add a checksum flag at the end of the command (for some cellphone use MTK chips, we should set this param true). + * How to know mtk chip: by getprop() method + * if ro.mediatek.gemini_support=true + * then + * it is mtk + * @return + * @throws Exception: will throw this exception for unsupported nfc tag + */ + public static TagUtil selectTag(Intent intent,boolean isCheckSUM) throws Exception + { + String action = intent.getAction(); + int type=0; + if (isSupportedAction(action)) { + // get TAG in the intent + Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + String[] tagTypes = tagFromIntent.getTechList();// 支持的类型集合 + String tagType = null; + for(int i=0;i0) + continue; + if (tagTypes != null && tagTypes.length > 0) { + tagType = tagFromIntent.getTechList()[i]; + } + if ("android.nfc.tech.NfcA".equals(tagType)) { + try{ + getTagUID_NfcA(tagFromIntent,isCheckSUM); + type=TAGUTIL_NfcA; + + }catch(Exception ex) + { + throw ex; + } + } + else + { + throw new Exception("unsupported action "+action +" only support ACTION_TECH_DISCOVERED or ACTION_TAG_DISCOVERED or ACTION_NDEF_DISCOVERED"); + } + } + tagFromIntent = null; + TagUtil tagUtil = new TagUtil(uid,type); + return tagUtil; +// if(checkTag(type)) +// { +// TagUtil tagUtil = new TagUtil(uid,type); +// return tagUtil; +// } +// else +// throw new Exception ("illegal tag"); + } + return null; + } + + /** + * read one page( four bytes in a page), + * @param intent + * @param addr: page address + * @param isCheckSum: + * @return 4 bytes array + * @throws AuthenticationException + * @throws Exception + */ + public byte[] readOnePage(Intent intent,byte addr,boolean isCheckSum) throws AuthenticationException,Exception + { + if(tagType==TagUtil.TAGUTIL_NfcA) + return readOnePage_NfcA( intent, addr,isCheckSum); + else + return null; + } + + + private byte[] readOnePage_NfcA(Intent intent,byte addr, boolean isCheckSum) throws AuthenticationException,Exception + { + String action = intent.getAction(); + byte[] result = null; + if (isSupportedAction(action)) { + //Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + //NfcA mfc = NfcA.get(tagFromIntent); + try { + if(authorised){ + //mfc.connect(); + //accreditation(mfc,secretKey);//认证 + byte[] data0 = new byte[2]; + byte[] dataWithCheckSum = new byte[4]; + data0[0] = 0x30; + data0[1] = addr; + byte[] data1; + if(isCheckSum) + { + byte[] checkSum = getCheckSum(data0); + dataWithCheckSum[0]=data0[0]; + dataWithCheckSum[1]=data0[1]; + dataWithCheckSum[2]=checkSum[0]; + dataWithCheckSum[3]=checkSum[1]; + data1 = nfcA.transceive(dataWithCheckSum);// read 4 pages one time + } + else + data1 = nfcA.transceive(data0);// read 4 pages one time + + result = new byte[4]; + if(data1.length<16) + throw new AuthenticationException("please authenticate first!"); + else + System.arraycopy(data1, 0, result, 0, 4);// get the first page + }else{ + throw new AuthenticationException("Authenticate First!"); + } + } catch (Exception e) { + throw e; + } +// finally { +// try { +// mfc.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } + } + return result; + } + + /** + * read four pages( 4 bytes in a page) + * @param intent + * @param addr: + * @param isCheckSum: + * @return 16 bytes array + * @throws AuthenticationException + * @throws Exception + */ + public byte[] readFourPage(Intent intent,byte addr, boolean isCheckSum) throws AuthenticationException,Exception + { + if(tagType==TagUtil.TAGUTIL_NfcA) + return readFourPage_NfcA( intent, addr,isCheckSum); + else + return null; + } + + + private byte[] readFourPage_NfcA(Intent intent,byte addr, boolean isCheckSum) throws AuthenticationException,Exception + { + String action = intent.getAction(); + byte[] result = null; + if (isSupportedAction(action)) { + //Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + //NfcA mfc = NfcA.get(tagFromIntent); + try { + if(authorised){ + //mfc.connect(); + //accreditation(mfc,secretKey); + byte[] data0 = new byte[2]; + byte[] dataWithCheckSum = new byte[4]; + data0[0] = 0x30; + data0[1] = addr; + byte[] data1; + if(isCheckSum) + { + byte[] checkSum = getCheckSum(data0); + dataWithCheckSum[0]=data0[0]; + dataWithCheckSum[1]=data0[1]; + dataWithCheckSum[2]=checkSum[0]; + dataWithCheckSum[3]=checkSum[1]; + result = nfcA.transceive(dataWithCheckSum);// 每次读出来的数据为4page的数据 + } + else + result = nfcA.transceive(data0);// 每次读出来的数据为4page的数据 + }else{ + throw new AuthenticationException("please authenticate first!"); + } + } catch (Exception e) { + throw e; + } +// finally { +// try { +// mfc.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } + } + return result; + } + + /** + * write a page(4 bytes in a page) + * @param intent + * @param addr + * @param contents 4 bytes array + * @param isCheckSum: + * @return true for success, false for failed + * @throws AuthenticationException + * @throws Exception + */ + public boolean writeTag(Intent intent, byte addr, byte[] contents, boolean isCheckSum) throws AuthenticationException, Exception + { + if(tagType==TagUtil.TAGUTIL_NfcA) + return writeAble( intent, addr,contents, isCheckSum); + else + return false; + } + + private boolean writeAble(Intent intent, byte addr, byte contents[], boolean isCheckSum) + throws AuthenticationException, Exception + { + boolean res = false; + if((new Integer(addr)).intValue() < 4) + throw new AuthenticationException("page no should bigger than 4"); + try + { + byte newByteArray[] = appendByteArray(contents); + int pageNum = newByteArray.length / 4; + byte array[] = new byte[4]; + for(int i = 0; i < pageNum; i++) + { + array[0] = newByteArray[0 + 4 * i]; + array[1] = newByteArray[1 + 4 * i]; + array[2] = newByteArray[2 + 4 * i]; + array[3] = newByteArray[3 + 4 * i]; + try + { + writeTag_NfcA(intent, (byte)(addr + i), array, false); + } + catch(Exception e) + { + throw new Exception((new StringBuilder()).append("write page ").append(addr + i).append(" failed").toString()); + } + } + + res = true; + } + catch(Exception e) + { + e.printStackTrace(); + Log.e("xxx", e.getMessage()); + } + return res; + } + + private boolean writeTag_NfcA(Intent intent, byte addr, byte[] contents, boolean isCheckSum) throws AuthenticationException, Exception + { + boolean result = false; + String action = intent.getAction(); + if (isSupportedAction(action)) { +// Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); +// NfcA mfc = NfcA.get(tagFromIntent); + + try { + if(authorised){ + if(contents != null && contents.length== 4){//判断输入的数据 + //mfc.connect(); + //accreditation(mfc,secretKey);//认证 + byte[] data2 = new byte[6]; + byte[] dataWithCheckSum= new byte[8]; + data2[0] = (byte) 0xA2; + data2[1] = addr; + data2[2] = contents[0]; + data2[3] = contents[1]; + data2[4] = contents[2]; + data2[5] = contents[3]; + byte[] data3; + if(isCheckSum) + { + byte[] checkSum = getCheckSum(data2); + dataWithCheckSum[0]=data2[0]; + dataWithCheckSum[1]=data2[1]; + dataWithCheckSum[2]=data2[2]; + dataWithCheckSum[3]=data2[3]; + dataWithCheckSum[4]=data2[4]; + dataWithCheckSum[5]=data2[5]; + dataWithCheckSum[6]=checkSum[0]; + dataWithCheckSum[7]=checkSum[1]; + data3 = nfcA.transceive(dataWithCheckSum);// 每次读出来的数据为4page的数据 + } + else + data3 = nfcA.transceive(data2); + result=true; + }else{ + throw new AuthenticationException("contents must be four bytes"); + } + } + else + { + throw new AuthenticationException("please authenticate first!"); + } + } catch (Exception e) { + throw e; + } +// finally { +// try { +// mfc.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } + } + return result; + } + + private static boolean isSupportedAction(String action) { + return NfcAdapter.ACTION_TECH_DISCOVERED.equals(action) || NfcAdapter.ACTION_TAG_DISCOVERED.equals(action) || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action); + } + + + /** + * 认证 + * @param intent + * @param key 秘钥 16 个字符(字母和数字)。 + * @param isCheckSum: 是否增加校验位 + * @return + * @throws AuthenticationException + */ + public boolean authentication(Intent intent, String key, boolean isCheckSum) throws AuthenticationException, Exception + { + String dexString = this.bytesToHexString(key.getBytes()); + return authentication_internal(intent,dexString,isCheckSum); + } + + /** + * 认证 + * @param intent + * @param key 秘钥 16 个字符(字母和数字)。 + * @param isCheckSum: 是否增加校验位 + * @return + * @throws AuthenticationException + */ + public boolean authentication(Intent intent, byte[] key, boolean isCheckSum) throws AuthenticationException, Exception + { + String dexString = this.bytesToHexString(key); + return authentication_internal(intent,dexString,isCheckSum); + } + /** + * 认证 + * @param intent + * @param key 秘钥 16 个字节, 用 32 个 16 进制字符的字符串表示。 + * @param isCheckSum: 是否增加校验位 + * @return + * @throws AuthenticationException + */ + public boolean authentication_internal(Intent intent, String key, boolean isCheckSum) throws AuthenticationException, Exception + { + if(tagType==TagUtil.TAGUTIL_NfcA) + return authentication_NfcA( intent, key,isCheckSum); + else + throw new Exception("unknow tag Type"+ tagType+". or SelectTag first."); + } + + private boolean authentication_NfcA(Intent intent, String key, boolean isCheckSum) throws AuthenticationException + { + boolean result = false; + String action = intent.getAction(); + if (isSupportedAction(action)) { +// Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); +// NfcA mfc = NfcA.get(tagFromIntent); + try { + //Log.e("aaa",key); + if(key != null && key.length() == 32){//判断输入的数据 + byte[] data = new byte[24]; + byte[] binaryKey = hexStringToBytes(key); + System.arraycopy(binaryKey, 0, data, 0, 16); + System.arraycopy(binaryKey, 0, data, 16, 8); + //mfc.connect(); + accreditation(nfcA,data,isCheckSum);//认证 + authorised=true; + secretKey = data; + return true; + }else{ + ERR_MSG = "key must be 32 hex chars,current key is "+key; + return false; + } + } catch (Exception e) { + e.printStackTrace(); + return false; + } + finally + { +// try { +// mfc.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } + } + }else + { + ERR_MSG=action+ " is not support"+ ", action must be on of ACTION_TECH_DISCOVERED or ACTION_TAG_DISCOVERED"; + return false; + } + } + + /** + * 从第 0 页开始,读取指定页数的数据,返回一个字节数组(1 页 4 个字节) + * @param intent: + * @param pageNums: 指定页数 + * @param isCheckSum: 是否增加校验位 + * @return + * @throws Exception + */ + public byte[] readAllPages(Intent intent,int pageNums,boolean isCheckSum) throws Exception{ + + if(tagType==TagUtil.TAGUTIL_NfcA) + return readAllPages_NfcA( intent, pageNums,isCheckSum); + + else + return null; + } + + + private byte[] readAllPages_NfcA(Intent intent, int pageNums,boolean isCheckSum) throws Exception{ +// Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); +// NfcA mfc = NfcA.get(tag); + int byteNum =pageNums*4; // 4 bytes a page + byte[] result= new byte[byteNum]; + try { + if(authorised){ + //mfc.connect(); + for (int i = 0x04; i < byteNum/4; i++) { + byte[] data0 = new byte[2]; + byte[] dataWithCheckSum = new byte[4]; + data0[0] = 0x30; + data0[1] = (byte) i; + + byte[] data1; + if(isCheckSum) + { + byte[] checkSum = getCheckSum(data0); + dataWithCheckSum[0]=data0[0]; + dataWithCheckSum[1]=data0[1]; + dataWithCheckSum[2]=checkSum[0]; + dataWithCheckSum[3]=checkSum[1]; + data1 = nfcA.transceive(dataWithCheckSum);// 每次读出来的数据为4page的数据 + } + else + data1 = nfcA.transceive(data0);// 4 pages + if(data1.length>=4) + System.arraycopy(data1, 0, result, 4*i, 4);// get one page + else + throw new Exception("read the" +i +"th page failed! "+data1.length +"bytes was read"); + } + return result; + }else{ + throw new AuthenticationException("please authenticate first!"); + } + } catch (Exception e) { + throw e; + } +// finally { +// try { +// mfc.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } + } + + /** + * 改变秘钥 + * @param intent + * @param newKey 新的秘钥 + * @param isCheckSum: 是否增加校验位 + * @return + * @throws AuthenticationException + * @throws Exception + */ + public boolean writeNewKey(Intent intent,String newKey,boolean isCheckSum) throws AuthenticationException,Exception + { + if(newKey != null && newKey.length() == 32){ + if(tagType==TagUtil.TAGUTIL_NfcA) + return writeNewKey_NfcA( intent,newKey,isCheckSum); + else + return false; + }else + throw new Exception("key must be 32 hex chars"); + } + + + // 写入密钥 + private boolean writeNewKey_NfcA(Intent intent,String newKey, boolean isCheckSum) throws Exception{ + boolean result = false; + String action = intent.getAction(); + if (isSupportedAction(action)) { +// Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); +// NfcA mfc = NfcA.get(tagFromIntent); + try { + if(authorised){ + String dataString = newKey; + //判断输入的数据 + + byte[] dataX = hexStringToBytes(dataString); + byte[] dataY = new byte[16]; + for(int i=0;i<16;i++){ + dataY[i] = dataX[15-i]; + System.out.println("mi"+dataY[i]); + } + byte[] data1 = new byte[6]; + byte[] data1WithCheckSum= new byte[8]; + data1[0] = (byte) 0xA2; + data1[1] = (byte) 0x2C; + System.arraycopy(dataY, 8, data1, 2, 4); + + byte[] data2 = new byte[6]; + byte[] data2WithCheckSum= new byte[8]; + data2[0] = (byte) 0xA2; + data2[1] = (byte) 0x2D; + System.arraycopy(dataY, 12, data2, 2, 4); + + byte[] data3 = new byte[6]; + byte[] data3WithCheckSum= new byte[8]; + data3[0] = (byte) 0xA2; + data3[1] = (byte) 0x2E; + System.arraycopy(dataY, 0, data3, 2, 4); + + byte[] data4 = new byte[6]; + byte[] data4WithCheckSum= new byte[8]; + data4[0] = (byte) 0xA2; + data4[1] = (byte) 0x2F; + System.arraycopy(dataY, 4, data4, 2, 4); + +// mfc.connect(); +// accreditation(mfc,secretKeyDefault);//认证 + + + if(isCheckSum) + { + byte[] checkSum = getCheckSum(data1); + for(int i=0;i<6;i++) + data1WithCheckSum[i]=data1[i]; + data1WithCheckSum[6]=checkSum[0]; + data1WithCheckSum[7]=checkSum[1]; + nfcA.transceive(data1WithCheckSum);// 每次读出来的数据为4page的数据 + } + else + nfcA.transceive(data1); + + if(isCheckSum) + { + byte[] checkSum = getCheckSum(data2); + for(int i=0;i<6;i++) + data2WithCheckSum[i]=data2[i]; + data2WithCheckSum[6]=checkSum[0]; + data2WithCheckSum[7]=checkSum[1]; + nfcA.transceive(data2WithCheckSum);// 每次读出来的数据为4page的数据 + } + else + nfcA.transceive(data2); + + if(isCheckSum) + { + byte[] checkSum = getCheckSum(data3); + for(int i=0;i<6;i++) + data3WithCheckSum[i]=data3[i]; + data3WithCheckSum[6]=checkSum[0]; + data3WithCheckSum[7]=checkSum[1]; + nfcA.transceive(data3WithCheckSum);// 每次读出来的数据为4page的数据 + } + else + nfcA.transceive(data3); + + if(isCheckSum) + { + byte[] checkSum = getCheckSum(data4); + for(int i=0;i<6;i++) + data4WithCheckSum[i]=data4[i]; + data4WithCheckSum[6]=checkSum[0]; + data4WithCheckSum[7]=checkSum[1]; + nfcA.transceive(data4WithCheckSum);// 每次读出来的数据为4page的数据 + } + else + nfcA.transceive(data4); + result = true; + + }else{ + throw new AuthenticationException("please authenticate first!"); + } + }catch (NumberFormatException e) { + throw new Exception("new key: "+newKey+" is not correct." +" key must be 32 hex chars"); + } catch (Exception e) { + throw e; + } +// finally { +// try { +// mfc.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } + } + return result; + } + +// /** +// * 设置芯片的新密码(适用于FJ8216 和 NXP216 芯片 ),在调用本方法前,要先通过 authentication216 方法进行认证。 +// * @param intent +// * @param newPWD 四个字节的 byte 数组,新的密码 +// * @param PACK 两个字节的 byte 数组,用于设置验证成功后的返回值。 +// * @param isCheckSum +// * @return +// * @throws AuthenticationException +// * @throws Exception +// */ +// public boolean writePWD216(Intent intent,byte[] newPWD,byte[] PACK, boolean isCheckSum) throws AuthenticationException,Exception +// { +// if(newPWD != null && newPWD.length == 4 && PACK.length==2){ +// if(tagType==TagUtil.TAGUTIL_NfcA) +// return writeNewKey216_NfcA( intent,newPWD,PACK,isCheckSum); +// else if (tagType==TagUtil.TAGUTIL_TYPE_ULTRALIGHT) +// return writeNewKey216_MifareUltraLight( intent,newPWD,PACK,isCheckSum); +// else if (tagType==TagUtil.TAGUTIL_TYPE_CLASSIC) +// return writeNewKey216_MifareClassic( intent,newPWD,PACK,isCheckSum); +// else +// return false; +// }else +// throw new Exception("new PWD must be 4 bytes and PACK must be 2 bytes"); +// } +// +// private boolean writeNewKey216_MifareUltraLight(Intent intent, byte[] newPWD,byte[] PACK,boolean isCheckSum) throws Exception{ +// byte[] oldE6 = readOnePage(intent, (byte)0XE6, isCheckSum); +// byte[] newE6 = new byte[4]; +// newE6[0]=PACK[0]; +// newE6[1]=PACK[1]; +// newE6[2]=oldE6[2]; +// newE6[3]=oldE6[3]; +// boolean result1 = writeTag(intent, (byte)0XE5, newPWD, isCheckSum); +// boolean result2 = writeTag(intent, (byte)0XE6, newE6, isCheckSum); +// return result1 && result2 ; +// } +// +// private boolean writeNewKey216_MifareClassic(Intent intent, byte[] newPWD,byte[] PACK,boolean isCheckSum) throws Exception{ +// throw new Exception("unimplemented"); +// } +// +// private boolean writeNewKey216_NfcA(Intent intent, byte[] newPWD,byte[] PACK, +// boolean isCheckSum) throws Exception { +// byte[] oldE6 = readOnePage(intent, (byte)0XE6, isCheckSum); +// byte[] newE6 = new byte[4]; +// newE6[0]=PACK[0]; +// newE6[1]=PACK[1]; +// newE6[2]=oldE6[2]; +// newE6[3]=oldE6[3]; +// boolean result1 = writeTag(intent, (byte)0XE5, newPWD, isCheckSum); +// boolean result2 = writeTag(intent, (byte)0XE6, newE6, isCheckSum); +// return result1 && result2 ; +// } + + /** + * 设置一个开始页面地址和访问方式, 该页面地址后的页面都需要认证后才可访问。访问方式有两种,0为读写访问,1 为写访问 + * @param intent + * @param addr: 访问地址, + * @param access: 如果设置为 0, 读写操作都需要授权. 如果设置为1 ,只有写操作需要授权。 + * @param isCheckSum: 是否增加校验位 + * @return + * @throws Exception + */ + public boolean setAccess(Intent intent,byte addr, int access,boolean isCheckSum) throws Exception + { + if(tagType==TagUtil.TAGUTIL_NfcA) + return setAccess_NfcA( intent,addr, access,isCheckSum); + else + throw new Exception("unknow tag Type"+ tagType+". or SelectTag first."); + } + + + private boolean setAccess_NfcA(Intent intent, byte addr, int access, boolean isCheckSum ) throws Exception { + if(addr< (byte)3 || addr > 0x30) + throw new Exception("address must between 03h and 30h"); + else + { + int value = (int)addr<<24; + access = access << 24; + boolean result1 = writeTag(intent, (byte)0X2A, this.getBytesArray(value), isCheckSum); + boolean result2 = writeTag(intent, (byte)0X2B, this.getBytesArray(access), isCheckSum); + if(result1 && result2) + return true; + else + return false; + } + } + + /** + * 锁定加锁位 + * @param intent + * @param isCheckSum 是否在命令后自动增加校验位。 + * @return + * @throws Exception + */ + public boolean lockLockingbits(Intent intent, boolean isCheckSum) throws Exception + { + if(tagType==TagUtil.TAGUTIL_NfcA) + return lockLockingbits_NfcA( intent,isCheckSum); + else + throw new Exception("unknow tag Type"+ tagType+". or SelectTag first."); + } + private boolean lockLockingbits_NfcA(Intent intent,boolean isCheckSum) throws Exception { + byte[] contents1= new byte[4]; + contents1[0]=(byte)0; + contents1[1]=(byte)0; + contents1[2]=(byte)7; + contents1[3]=(byte)0; + + byte[] contents2= new byte[4]; + contents2[0]=(byte)17; + contents2[1]=(byte)15; + contents2[2]=(byte)0; + contents2[3]=(byte)0; + + if (writeTag(intent, (byte)2, contents1,isCheckSum) && writeTag(intent, (byte)40, contents2,isCheckSum)) + return true; + else + return false; + } + + /** + * 锁定一个页面范围 + * @param intent + * @param addr1 要锁定的开始页面 + * @param addr2 要锁定的结束页面 + * @param isCheckSum 是否在命令后自动增加校验位。 + * @return + * @throws Exception + */ + public boolean lockPage(Intent intent,byte addr1 ,byte addr2, boolean isCheckSum) throws Exception + { + if(tagType==TagUtil.TAGUTIL_NfcA) + return lockPage_NfcA( intent,addr1, addr2, isCheckSum); + else + throw new Exception("unknow tag Type"+ tagType+". or SelectTag first."); + } + + private boolean lockPage_NfcA(Intent intent, byte startAddr1, byte endAddr, boolean isCheckSum) throws Exception { + boolean result = false; + + if(startAddr1>endAddr) + { + throw new Exception ("endAdddr must greater than or equal to startAddr"); + } + + if(startAddr1<3 || endAddr >47) + { + throw new Exception ("startAddr and endAdddr must between [3,47]"); + } + + if(endAddr>15) + { + if(lockPage_NfcA_Part1(intent,startAddr1,(byte)15, isCheckSum) && lockPage_NfcA_Part2(intent,(byte)16,endAddr, isCheckSum)) + result = true; + } + else + { + if(lockPage_NfcA_Part1(intent,startAddr1,(byte)15, isCheckSum)) + result=true; + } + return result; + } + + /** + * 锁定为在第 40 页, 可以锁定的范围是 16 到 47 页面。 + * lock in page 40, lock address between 16 and 47 + * @param intent + * @param startAddr + * @param endAddr + * @param isCheckSum 是否在命令后自动增加校验位。 + */ + private boolean lockPage_NfcA_Part2(Intent intent, byte startAddr, byte endAddr, boolean isCheckSum) throws Exception{ + byte[] contents = new byte[4]; + int value=0; + int totalValue=0; + for(int j=startAddr;j<=endAddr;j++) + { + if(j<=39) + { + if(j%4>0) + continue; + int i=(j)/4; + switch (i) + { + case 4: + value=2^25; + break; + case 5: + value=2^26; + break; + case 6: + value=2^27; + break; + case 7: + value=2^29; + break; + case 8: + value=2^30; + break; + case 9: + value=2^31; + break; + default: + break; + } + }else + { + switch (j) + { + case 41: + value=2^20; + break; + case 42: + value=2^21; + break; + case 43: + value=2^22; + break; + case 44: + value=2^23; + break; + case 40: + case 45: + case 46: + case 47: + default: + + } + } + totalValue+=value; + } + contents = getBytesArray(totalValue); + return writeTag(intent, (byte)40, contents, isCheckSum); + } + + private byte[] getBytesArray(int value) { + byte[] contents = new byte[4]; + contents[0] = (byte)(value >>> 24); + contents[1] = (byte)(value >>> 16); + contents[2] = (byte)(value >>> 8); + contents[3] = (byte)(value ); + return contents; + } + + /** + * 锁定位在第 2 页面, 可以锁定的地址范围是 3 到 15 + * lock in page 2, lock address between 3 and 15 + * @param startAddr + * @param endAddr + * @param isCheckSum 是否在命令后自动增加校验位。 + */ + private boolean lockPage_NfcA_Part1(Intent intent, byte startAddr, byte endAddr, boolean isCheckSum) throws Exception{ + byte[] contents = new byte[4]; + int value=0; + int totalValue=0; + for(int i=startAddr;i<=endAddr;i++) + { + switch (i) + { + case 3: + value=2^8; + break; + case 4: + value=2^12; + break; + case 5: + value=2^13; + break; + case 6: + value=2^14; + break; + case 7: + value=2^15; + break; + case 8: + value=2^0; + break; + case 9: + value=2^1; + break; + case 10: + value=2^2; + break; + case 11: + value=2^3; + break; + case 12: + value=2^4; + break; + case 13: + value=2^5; + break; + case 14: + value=2^6; + break; + case 15: + value=2^7; + break; + default: + } + totalValue+=value; + } + contents[0] = (byte)(totalValue >>> 24); + contents[1] = (byte)(totalValue >>> 16); + contents[2] = (byte)(totalValue >>> 8); + contents[3] = (byte)(totalValue ); + return writeTag(intent, (byte)2, contents, isCheckSum); + } + + /** + * 锁定所有页面 + * @param intent + * @param isCheckSum 是否在命令后自动增加校验位。 + * @return + * @throws Exception + */ + public boolean lockPageAll(Intent intent, boolean isCheckSum) throws Exception + { + boolean result = false; + switch(tagType) + { + case TagUtil.TAGUTIL_NfcA: + result= lockPageAll_NfcA(intent, isCheckSum); + break; + case TagUtil.TAGUTIL_TYPE_ULTRALIGHT: + result= lockPageAll_MifareUltraLight(intent); + break; + case TagUtil.TAGUTIL_TYPE_CLASSIC: + result= lockPageAll_MifareClassic(intent); + break; + default: + throw new Exception("unknow tag Type"+ tagType+". or SelectTag first."); + }return result; + } + + + private boolean lockPageAll_NfcA(Intent intent, boolean isCheckSum) throws Exception + { + byte[] contents1= new byte[4]; + contents1[0]=(byte)0; + contents1[1]=(byte)0; + contents1[2]=(byte)255; + contents1[3]=(byte)255; + + byte[] contents2= new byte[4]; + contents2[0]=(byte)255; + contents2[1]=(byte)255; + contents2[2]=(byte)0; + contents2[3]=(byte)0; + + if (writeTag(intent, (byte)2, contents1, isCheckSum) && writeTag(intent, (byte)40, contents2, isCheckSum)) + return true; + else + return false; + } + + private boolean lockPageAll_MifareUltraLight(Intent intent) throws Exception + { + throw new Exception("unimplemented"); + } + + private boolean lockPageAll_MifareClassic(Intent intent) throws Exception + { + throw new Exception("unimplemented"); + } + + /** + * 获取标签类型,目前可支持的标签类型包括 NFCA 和 UltraLight + * @return + * @throws AuthenticationException + */ + public int getTagType() throws AuthenticationException + { + return tagType; + } + + private void accreditation(NfcA mfc,byte[] secretKeys,boolean isCheckSum) throws Exception { + byte[] iv = ivDefault; + + byte[] command0 = new byte[2];// 发送认证指令的参数 + byte[] command0WithCheckSum = new byte[4];// 发送认证指令的参数(with check sum) + + byte[] command1 = null;// 发送认证后,卡片返回的密文1 + byte[] command1WithCheckSum = null;// 发送认证后,卡片返回的密文1 + + byte[] command2 = null;// 密文1去掉数组中的第1个数据,取出有效数组 + + byte[] command3 = null;// 密文1 解密后的数据 + byte[] command4 = null;// command2 加密 + byte[] command5 = null;// command3 循环左移得到的数据 + byte[] command6 = null;// 使用command5 和 command4 第二次加密后的数据RNDB + byte[] command7 = null;// + byte[] command8 = null;// + byte[] command9 = null;// + byte[] command10 = null;// + byte[] command11 = null;// + + command0[0] = (byte) 0x1A; // 命令位 + command0[1] = (byte) 0x00; // 标志位 + if(isCheckSum) + { + byte[] checkSum = getCheckSum(command0); + command0WithCheckSum[0]=command0[0]; + command0WithCheckSum[1]=command0[1]; + command0WithCheckSum[2]=checkSum[0]; + command0WithCheckSum[3]=checkSum[1]; + command1WithCheckSum = mfc.transceive(command0WithCheckSum);// 11 bytes + if(command1WithCheckSum.length != 11) + { + String str=""; + for (int i = 0 ; i> 4)); + sb.append(hexString.charAt((bytes[i] & 15) >> 0)); + } + + return sb.toString(); + } + + public static String hexStringToString(String bytes) + { + String hexString = "0123456789ABCDEF"; + ByteArrayOutputStream baos = new ByteArrayOutputStream(bytes.length() / 2); + for(int i = 0; i < bytes.length(); i += 2) + baos.write(hexString.indexOf(bytes.charAt(i)) << 4 | hexString.indexOf(bytes.charAt(i + 1))); + + return new String(baos.toByteArray()); + } + + public static byte[] StringtoBytes(String str) + { + String hexstr = StringtoHexString(str); + byte byte1[] = hexStringToBytes(hexstr); + return byte1; + } + + private byte[] appendByteArray(byte byteArray[]) + { + int length = byteArray.length; + int m = length % 4; + byte newByteArray[]; + if(m == 0) + newByteArray = new byte[length]; + else + newByteArray = new byte[length + (4 - m)]; + System.arraycopy(byteArray, 0, newByteArray, 0, length); + return newByteArray; + } + + /** + * 字节数组转化为16进制字符串 + */ + public static String bytesToHexString(byte[] src) { + StringBuilder stringBuilder = new StringBuilder(); + if (src == null || src.length <= 0) { + return null; + } + for (int i = 0; i < src.length; i++) { + int v = src[i] & 0xFF; + String hv = Integer.toHexString(v); + if (hv.length() < 2) { + stringBuilder.append(0); + } + stringBuilder.append(hv); + } + return stringBuilder.toString(); + } + + private static void getTagUID_NfcA(Tag tag,boolean isCheckSum) throws Exception + { +// byte[] datau = tag.getId(); +// uid=bytesToHexString(datau); + + nfcA = NfcA.get(tag); + try { + String metaInfo = ""; + nfcA.connect(); + byte[] datas = new byte[2]; + byte[] datasWithCheckSum = new byte[4]; + datas[0] = 0x30; + datas[1] = 0x00; + byte[] datar; + if(isCheckSum) + { + byte[] checkSum = getCheckSum(datas); + datasWithCheckSum[0]=datas[0]; + datasWithCheckSum[1]=datas[1]; + datasWithCheckSum[2]=checkSum[0]; + datasWithCheckSum[3]=checkSum[1]; + datar = nfcA.transceive(datasWithCheckSum);// 每次读出来的数据为4page的数据 + } + else + datar = nfcA.transceive(datas);// 每次读出来的数据为4page的数据 + byte[] datau = new byte[7];//uid号 + System.arraycopy(datar, 0, datau, 0, 3);// 去4page中的第1page数据 + System.arraycopy(datar, 4, datau, 3, 4);// 去4page中的第1page数据 + uid=bytesToHexString(datau); + } + catch(Exception e) + { + e.printStackTrace(); + throw e; + } +// finally +// { +// try { +// mfc.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } + } + + private static void getFinalPage_NfcA(Tag tag,boolean isCheckSum) throws Exception + { + try { + //mfc.connect(); + byte[] datas = new byte[2]; + byte[] datasWithChcekSum = new byte[4]; + + datas[0] = 0x30; + datas[1] = (byte)0xFF; + byte[] checkSum; + + if(isCheckSum) + { + checkSum = getCheckSum(datas); + datasWithChcekSum[0]=datas[0]; + datasWithChcekSum[1]=datas[1]; + datasWithChcekSum[2]=checkSum[0]; + datasWithChcekSum[3]=checkSum[1]; + } + + if(nfcA==null) + { + nfcA = NfcA.get(tag); + nfcA.connect(); + } + + byte[] datar; + + if(isCheckSum) + datar = nfcA.transceive(datasWithChcekSum); + else + datar = nfcA.transceive(datas); + + byte[] datau = new byte[4];//uid号 + System.arraycopy(datar, 0, datau, 0, 3);// 4page中的第1page数据 + finalPage=bytesToHexString(datau); + } + catch(Exception e) + { + throw e; + } +// finally +// { +// try { +// mfc.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } + } + + public static String getUid() { + return uid; + } + + + /** + * 访问时需要认证的页面地址范围 + * @param intent + * @return 返回一个开始地址。访问该地址后的所有页面,都需要认证。 如果返回值大于等于 48 ,则 访问(读或写)所有页面都不需要认证。如果返回值小于48,说明访问(读或写)返回值地址到 48 之间的页面时,需要认证。 + * @throws Exception + */ + public int getAuthenticationAddr(Intent intent,boolean isCheckSum) throws Exception + { + byte[] result = readOnePage(intent, (byte)PAGE_ADDR_AUTH0,isCheckSum); + int r = result[0]; + return r; + } + + /** + * 如果访问时,有地址需要认证。该方法可以获得认证的种类 + * @param intent + * @param isCheckSum 是否在命令后自动增加校验位。 + * @return 如果返回值=0 ,则读写都需要认证。如果返回值=1,只有写需要认证 + * @throws Exception + */ + public int getAuthenticationType(Intent intent,boolean isCheckSum) throws Exception + { + byte[] result = readOnePage(intent, (byte)PAGE_ADDR_AUTH1,isCheckSum); + int r = result[0]; + return r; + } + + /** + * 关闭连接 + */ + public void close() + { + try { + if(nfcA!=null) + nfcA.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private static byte[] getCheckSum(byte[] byteAyyay) throws Exception { + AbstractChecksum checksum=null; + try { + String checksumArg="crc:16,1021,c6c6,true,true,0"; + checksum = JacksumAPI.getChecksumInstance(checksumArg,false); + } catch (NoSuchAlgorithmException nsae) { + throw new ExitException(nsae.getMessage()+"\nUse -a to specify a valid one.\nFor help and a list of all supported algorithms use -h.\nExit.", ExitStatus.PARAMETER); + } + checksum.setEncoding(AbstractChecksum.HEX); + //byte[] byteAyyay = hexStringToBytes(string); + checksum.update(byteAyyay); + String hexValue = checksum.getHexValue(); + //String resultStr =checksum.toString();//d97c 02a8 + byte[] result = reverse(hexStringToBytes(hexValue)); + return result; + } + + /** + * 使用命令 + * java -jar aofei_nfc.jar + * 获取版本号 + * @param args + */ + public static void main(String[] args) + { + System.out.println("2.2.0"); + } + + private static byte[] reverse(byte[] bytes) + { + byte[] result= new byte[bytes.length]; + + for(int i=0;iRename … Archive Backup -<<<<<<< HEAD Change Passphrase -======= Backup To NFC ->>>>>>> 6ce4b42f2effa3ec3525b3849c8f20ada88267d5 - Continue typing … Meh … C\'mon, you can do better! From 85d35d564ba110620cb74bc95002fa66f6c6ca8f Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 11 May 2018 12:10:23 +0800 Subject: [PATCH 07/12] Add Input NFC Password --- app/src/main/AndroidManifest.xml | 1 + .../m2049r/xmrwallet/GenerateFragment.java | 35 +++++ .../com/m2049r/xmrwallet/LoginActivity.java | 97 ++++++++++++- .../dialog/InputNfcPasswordFragment.java | 130 ++++++++++++++++++ app/src/main/res/drawable/nfc1.png | Bin 0 -> 17447 bytes app/src/main/res/drawable/nfc2.png | Bin 0 -> 17414 bytes app/src/main/res/drawable/nfc3.png | Bin 0 -> 17280 bytes app/src/main/res/drawable/nfc_signal.xml | 7 + app/src/main/res/layout/fragment_generate.xml | 67 +++++++++ .../layout/fragment_input_nfc_password.xml | 40 ++++++ app/src/main/res/values-de/strings.xml | 3 + app/src/main/res/values-es/strings.xml | 3 + app/src/main/res/values-fr/strings.xml | 3 + app/src/main/res/values-it/strings.xml | 3 + app/src/main/res/values-nb/strings.xml | 3 + app/src/main/res/values-zh-rTW/strings.xml | 3 + app/src/main/res/values/strings.xml | 4 + 17 files changed, 395 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/com/m2049r/xmrwallet/dialog/InputNfcPasswordFragment.java create mode 100644 app/src/main/res/drawable/nfc1.png create mode 100644 app/src/main/res/drawable/nfc2.png create mode 100644 app/src/main/res/drawable/nfc3.png create mode 100644 app/src/main/res/drawable/nfc_signal.xml create mode 100644 app/src/main/res/layout/fragment_input_nfc_password.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index aa708d26dc..7f10e51de7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -27,6 +27,7 @@ diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java index 82bc40a822..3edb1ae49d 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java @@ -19,6 +19,7 @@ import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; +import android.graphics.drawable.AnimationDrawable; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.design.widget.TextInputLayout; @@ -35,6 +36,7 @@ import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.widget.Button; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.Switch; import android.widget.TextView; @@ -71,6 +73,15 @@ public class GenerateFragment extends Fragment { private TextInputLayout etWalletViewKey; private TextInputLayout etWalletSpendKey; private TextInputLayout etWalletRestoreHeight; + + private LinearLayout llNfcPasswordSeed; + private TextInputLayout etNfcPasswordSeed; + private ImageView ivNfcPasswordSeed; + + private LinearLayout llNfcPasswordkey; + private TextInputLayout etNfcPasswordkey; + private ImageView ivNfcPasswordkey; + private Button bGenerate; private String type = null; @@ -92,8 +103,26 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, etWalletViewKey = (TextInputLayout) view.findViewById(R.id.etWalletViewKey); etWalletSpendKey = (TextInputLayout) view.findViewById(R.id.etWalletSpendKey); etWalletRestoreHeight = (TextInputLayout) view.findViewById(R.id.etWalletRestoreHeight); + + llNfcPasswordSeed = (LinearLayout) view.findViewById(R.id.llNfcPasswordSeed); + etNfcPasswordSeed = (TextInputLayout) view.findViewById(R.id.etNfcPasswordSeed); + ivNfcPasswordSeed = (ImageView) view.findViewById(R.id.ivNfcPasswordSeed); + llNfcPasswordkey = (LinearLayout) view.findViewById(R.id.llNfcPasswordkey); + etNfcPasswordkey = (TextInputLayout) view.findViewById(R.id.etNfcPasswordkey); + ivNfcPasswordkey = (ImageView) view.findViewById(R.id.ivNfcPasswordkey); + bGenerate = (Button) view.findViewById(R.id.bGenerate); + ivNfcPasswordkey.setImageResource(R.drawable.nfc_signal); + ivNfcPasswordSeed.setImageResource(R.drawable.nfc_signal); + AnimationDrawable nfcAnimationkey = (AnimationDrawable) ivNfcPasswordkey.getDrawable(); + nfcAnimationkey.setOneShot(false); + nfcAnimationkey.start(); + + AnimationDrawable nfcAnimationSeed = (AnimationDrawable) ivNfcPasswordSeed.getDrawable(); + nfcAnimationSeed.setOneShot(false); + nfcAnimationSeed.start(); + etWalletMnemonic.getEditText().setRawInputType(InputType.TYPE_CLASS_TEXT); etWalletAddress.getEditText().setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); etWalletViewKey.getEditText().setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); @@ -212,6 +241,8 @@ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { return false; } }); + llNfcPasswordSeed.setVisibility(View.VISIBLE); + } else if (type.equals(TYPE_KEY) || type.equals(TYPE_VIEWONLY)) { etWalletPassword.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { @@ -252,6 +283,8 @@ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { return false; } }); + + llNfcPasswordkey.setVisibility(View.VISIBLE); } if (type.equals(TYPE_KEY)) { etWalletSpendKey.setVisibility(View.VISIBLE); @@ -268,6 +301,7 @@ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { return false; } }); + llNfcPasswordkey.setVisibility(View.VISIBLE); } if (!type.equals(TYPE_NEW)) { etWalletRestoreHeight.setVisibility(View.VISIBLE); @@ -281,6 +315,7 @@ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { return false; } }); + } bGenerate.setOnClickListener(new View.OnClickListener() diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java index 782afba6e2..771106b2cd 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java @@ -18,6 +18,7 @@ import android.app.Activity; import android.app.AlertDialog; +import android.app.PendingIntent; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; @@ -25,6 +26,7 @@ import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.media.MediaScannerConnection; +import android.nfc.NfcAdapter; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; @@ -45,6 +47,7 @@ import com.m2049r.xmrwallet.dialog.AboutFragment; import com.m2049r.xmrwallet.dialog.CreditsFragment; import com.m2049r.xmrwallet.dialog.HelpFragment; +import com.m2049r.xmrwallet.dialog.InputNfcPasswordFragment; import com.m2049r.xmrwallet.dialog.PrivacyFragment; import com.m2049r.xmrwallet.model.NetworkType; import com.m2049r.xmrwallet.model.Wallet; @@ -82,6 +85,10 @@ public class LoginActivity extends SecureActivity private Toolbar toolbar; + private TagUtil tagUtil ; + + private InputNfcPasswordFragment inputNfcPassword; + @Override public void setToolbarButton(int type) { toolbar.setButton(type); @@ -323,7 +330,6 @@ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { return false; } }); - dialog.show(); } @@ -379,6 +385,36 @@ protected void onPostExecute(Boolean result) { } } + private class AsyncBackupToNFC2 extends AsyncTask { + @Override + protected void onPreExecute() { + super.onPreExecute(); + + } + + @Override + protected Boolean doInBackground(Object... params) { + if (params.length != 2) return false; + Intent intent = (Intent) params[0]; + String walletName = (String) params[1]; + return backupWalletToNFC(intent,walletName); + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + if (isDestroyed()) { + return; + } + dismissProgressDialog(); + if (!result) { + Toast.makeText(LoginActivity.this, getString(R.string.backup_failed), Toast.LENGTH_LONG).show(); + }else{ + InputNfcPasswordFragment.getInstance().dismiss(); + } + } + } + private boolean backupWallet(String walletName) { File backupFolder = new File(getStorageRoot(), "backups"); if (!backupFolder.exists()) { @@ -400,6 +436,42 @@ private boolean backupWallet(String walletName) { return success; } + private boolean backupWalletToNFC(Intent intent,String walletName) { + boolean success=false; + File walletFile = Helper.getWalletFile(LoginActivity.this, walletName); + //Timber.d("backup " + walletFile.getAbsolutePath() + " to " + backupFile.getAbsolutePath()); + + boolean authenticated=false; + try { + tagUtil = TagUtil.selectTag(intent, false); + authenticated = tagUtil.authentication(intent, getKey(), false); + }catch (Exception e) { + e.printStackTrace(); + } + + if(authenticated) + { + Toast toast = Toast.makeText(LoginActivity.this, "Authentication Successful", Toast.LENGTH_LONG); + try { + + byte[] bytes = new byte[(int)walletFile.length()]; + new FileInputStream(walletFile).read(bytes); + tagUtil.writeTag(intent,(byte)4,bytes,false); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (AuthenticationException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + } + else + return false; + return success; + } + private boolean backupWalletToNFC(String walletName) { boolean success=false; File walletFile = Helper.getWalletFile(LoginActivity.this, walletName); @@ -432,8 +504,8 @@ private boolean backupWalletToNFC(String walletName) { e.printStackTrace(); } } - else - return false; + else + return false; return success; } private byte[] getKey() @@ -475,7 +547,9 @@ public void onWalletBackupToFile(String walletName) { @Override public void onWalletBackupToNFC(String walletName) { Timber.d("backup to NFC hard wallet for wallet ." + walletName + "."); - new AsyncBackupToNFC().execute(walletName); + InputNfcPasswordFragment.display(getSupportFragmentManager()); + InputNfcPasswordFragment.getInstance().setWalletName(walletName); + } private class AsyncArchive extends AsyncTask { @@ -659,6 +733,21 @@ protected void onResume() { } } + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction()) + || NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction()) + || NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) { + try { + tagUtil = TagUtil.selectTag(intent, false); + new AsyncBackupToNFC2().execute(intent,InputNfcPasswordFragment.getInstance().getWalletName()); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + private class MyProgressDialog extends ProgressDialog { Activity activity; diff --git a/app/src/main/java/com/m2049r/xmrwallet/dialog/InputNfcPasswordFragment.java b/app/src/main/java/com/m2049r/xmrwallet/dialog/InputNfcPasswordFragment.java new file mode 100644 index 0000000000..16ff8933e1 --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/dialog/InputNfcPasswordFragment.java @@ -0,0 +1,130 @@ +package com.m2049r.xmrwallet.dialog; + + +import android.app.AlertDialog; +import android.app.Dialog; +import android.graphics.drawable.AnimationDrawable; +import android.os.Bundle; +import android.support.design.widget.TextInputLayout; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; + +import com.m2049r.xmrwallet.R; +import com.nulabinc.zxcvbn.Strength; +import com.nulabinc.zxcvbn.Zxcvbn; + +public class InputNfcPasswordFragment extends DialogFragment { + + private String walletName = null; + + + static final String TAG = "InputNfcPasswordFragment"; + + private TextInputLayout etWalletPassword; + + private static InputNfcPasswordFragment fragment = null; + + + public static InputNfcPasswordFragment getInstance() { + if(fragment ==null){ + fragment = new InputNfcPasswordFragment(); + } + + return fragment; + } + + public static void display(FragmentManager fm) { + FragmentTransaction ft = fm.beginTransaction(); + Fragment prev = fm.findFragmentByTag(TAG); + if (prev != null) { + ft.remove(prev); + } + InputNfcPasswordFragment.getInstance().show(ft, TAG); + } + + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_input_nfc_password, null); + etWalletPassword = (TextInputLayout) view.findViewById(R.id.etWalletPassword); + etWalletPassword.getEditText().addTextChangedListener(new TextWatcher() { + @Override + public void afterTextChanged(Editable editable) { + checkPassword(); + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + }); + etWalletPassword.requestFocus(); + ImageView nfcAnimationImage = (ImageView) view.findViewById(R.id.NfcAnimation); + nfcAnimationImage.setImageResource(R.drawable.nfc_signal); + AnimationDrawable nfcAnimation = (AnimationDrawable) nfcAnimationImage.getDrawable(); + nfcAnimation.setOneShot(false); + nfcAnimation.start(); + + + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setView(view); + initZxcvbn(); + return builder.create(); + } + Zxcvbn zxcvbn = new Zxcvbn(); + // initialize zxcvbn engine in background thread + private void initZxcvbn() { + new Thread(new Runnable() { + @Override + public void run() { + zxcvbn.measure(""); + } + }).start(); + } + + private void checkPassword() { + String password = etWalletPassword.getEditText().getText().toString(); + if (!password.isEmpty()) { + Strength strength = zxcvbn.measure(password); + int msg; + double guessesLog10 = strength.getGuessesLog10(); + if (guessesLog10 < 10) + msg = R.string.password_weak; + else if (guessesLog10 < 11) + msg = R.string.password_fair; + else if (guessesLog10 < 12) + msg = R.string.password_good; + else if (guessesLog10 < 13) + msg = R.string.password_strong; + else + msg = R.string.password_very_strong; + etWalletPassword.setError(getResources().getString(msg)); + } else { + etWalletPassword.setError(null); + } + } + + //get input nfc password + public String getPassword(){ + String password = etWalletPassword.getEditText().getText().toString(); + return password; + } + + public String getWalletName() { + return walletName; + } + + public void setWalletName(String walletName) { + this.walletName = walletName; + } +} diff --git a/app/src/main/res/drawable/nfc1.png b/app/src/main/res/drawable/nfc1.png new file mode 100644 index 0000000000000000000000000000000000000000..1cf6f9253f3d349cd23fd87d637ca3cc54ad6890 GIT binary patch literal 17447 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/nfc2.png b/app/src/main/res/drawable/nfc2.png new file mode 100644 index 0000000000000000000000000000000000000000..f971f19e6b2f2d074e6cda30cacda6ef3f2ef8f7 GIT binary patch literal 17414 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/nfc3.png b/app/src/main/res/drawable/nfc3.png new file mode 100644 index 0000000000000000000000000000000000000000..50d8d85811bacebfd05a5c9892c9be58072dffcf GIT binary patch literal 17280 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/nfc_signal.xml b/app/src/main/res/drawable/nfc_signal.xml new file mode 100644 index 0000000000..aeb282c9a7 --- /dev/null +++ b/app/src/main/res/drawable/nfc_signal.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_generate.xml b/app/src/main/res/layout/fragment_generate.xml index 7a8df46466..9df86d946a 100644 --- a/app/src/main/res/layout/fragment_generate.xml +++ b/app/src/main/res/layout/fragment_generate.xml @@ -169,6 +169,73 @@ android:textAlignment="textStart" /> + + + + + + + + + + + + + + + + + + + + +