From d2f07ba3b6bcc6f2b39269aff5a49e5f1144b6bb Mon Sep 17 00:00:00 2001 From: m2049r <m2049r@monerujo.io> Date: Thu, 15 Apr 2021 19:10:01 +0200 Subject: [PATCH] migrate to scoped storage and API 30 (#741) --- app/build.gradle | 8 +- app/src/main/AndroidManifest.xml | 1 - .../com/m2049r/xmrwallet/LoginActivity.java | 521 +++++++----------- .../com/m2049r/xmrwallet/LoginFragment.java | 121 ++-- .../xmrwallet/layout/WalletInfoAdapter.java | 13 +- .../m2049r/xmrwallet/model/WalletManager.java | 53 +- .../com/m2049r/xmrwallet/util/Helper.java | 44 +- .../m2049r/xmrwallet/util/KeyStoreHelper.java | 8 +- .../xmrwallet/util/LegacyStorageHelper.java | 170 ++++++ .../com/m2049r/xmrwallet/util/ZipBackup.java | 64 +++ .../com/m2049r/xmrwallet/util/ZipRestore.java | 139 +++++ app/src/main/res/layout/item_wallet.xml | 12 +- app/src/main/res/menu/list_context_menu.xml | 2 +- app/src/main/res/menu/list_menu.xml | 5 + app/src/main/res/values-cat/strings.xml | 13 +- app/src/main/res/values-de/strings.xml | 12 +- app/src/main/res/values-el/strings.xml | 13 +- app/src/main/res/values-eo/strings.xml | 13 +- app/src/main/res/values-es/strings.xml | 13 +- app/src/main/res/values-et/strings.xml | 13 +- app/src/main/res/values-fr/strings.xml | 13 +- app/src/main/res/values-hu/strings.xml | 13 +- app/src/main/res/values-it/strings.xml | 13 +- app/src/main/res/values-ja/strings.xml | 13 +- app/src/main/res/values-nb/strings.xml | 13 +- app/src/main/res/values-nl/strings.xml | 13 +- app/src/main/res/values-pt-rBR/strings.xml | 13 +- app/src/main/res/values-pt/strings.xml | 13 +- app/src/main/res/values-ro/strings.xml | 13 +- app/src/main/res/values-ru/strings.xml | 13 +- app/src/main/res/values-sk/strings.xml | 13 +- app/src/main/res/values-sr/strings.xml | 13 +- app/src/main/res/values-sv/strings.xml | 13 +- app/src/main/res/values-uk/strings.xml | 13 +- app/src/main/res/values-zh-rCN/strings.xml | 13 +- app/src/main/res/values-zh-rTW/strings.xml | 13 +- app/src/main/res/values/strings.xml | 20 +- 37 files changed, 842 insertions(+), 624 deletions(-) create mode 100644 app/src/main/java/com/m2049r/xmrwallet/util/LegacyStorageHelper.java create mode 100644 app/src/main/java/com/m2049r/xmrwallet/util/ZipBackup.java create mode 100644 app/src/main/java/com/m2049r/xmrwallet/util/ZipRestore.java diff --git a/app/build.gradle b/app/build.gradle index 0ee925bb..400d9ef9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,14 +1,14 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 29 + compileSdkVersion 30 buildToolsVersion '29.0.3' defaultConfig { applicationId "com.m2049r.xmrwallet" minSdkVersion 21 - targetSdkVersion 29 - versionCode 801 - versionName "1.18.1 'ChAdOx1'" + targetSdkVersion 30 + versionCode 900 + versionName "1.19.0.2 'XXX'" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" externalNativeBuild { cmake { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5f9acf44..a18294ff 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,7 +8,6 @@ <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.USE_BIOMETRIC" /> - <uses-permission android:name="android.permission.USE_FINGERPRINT" /> <uses-permission android:name="android.permission.NFC" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java index be6dd760..4e8895ee 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java @@ -23,10 +23,8 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; -import android.content.pm.PackageManager; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; -import android.media.MediaScannerConnection; import android.os.AsyncTask; import android.os.Bundle; import android.view.KeyEvent; @@ -39,6 +37,7 @@ import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatDelegate; import androidx.fragment.app.Fragment; @@ -62,10 +61,13 @@ import com.m2049r.xmrwallet.service.WalletService; import com.m2049r.xmrwallet.util.DayNightMode; import com.m2049r.xmrwallet.util.Helper; import com.m2049r.xmrwallet.util.KeyStoreHelper; +import com.m2049r.xmrwallet.util.LegacyStorageHelper; import com.m2049r.xmrwallet.util.LocaleHelper; import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor; import com.m2049r.xmrwallet.util.NightmodeHelper; import com.m2049r.xmrwallet.util.ThemeHelper; +import com.m2049r.xmrwallet.util.ZipBackup; +import com.m2049r.xmrwallet.util.ZipRestore; import com.m2049r.xmrwallet.widget.Toolbar; import java.io.File; @@ -290,9 +292,6 @@ public class LoginActivity extends BaseActivity 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 = findViewById(R.id.toolbar); @@ -319,11 +318,9 @@ public class LoginActivity extends BaseActivity loadFavouritesWithNetwork(); - if (Helper.getWritePermission(this)) { - if (savedInstanceState == null) startLoginFragment(); - } else { - Timber.i("Waiting for permissions"); - } + LegacyStorageHelper.migrateWallets(this); + + if (savedInstanceState == null) startLoginFragment(); // try intents Intent intent = getIntent(); @@ -396,53 +393,25 @@ public class LoginActivity extends BaseActivity .show(); } - private class AsyncRename extends AsyncTask<String, Void, Boolean> { - @Override - protected void onPreExecute() { - super.onPreExecute(); - showProgressDialog(R.string.rename_progress); - } - - @Override - protected Boolean doInBackground(String... params) { - if (params.length != 2) return false; - String oldName = params[0]; - String newName = params[1]; - File walletFile = Helper.getWalletFile(LoginActivity.this, oldName); - boolean success = renameWallet(walletFile, newName); - try { - if (success) { - String savedPass = KeyStoreHelper.loadWalletUserPass(LoginActivity.this, oldName); - KeyStoreHelper.saveWalletUserPass(LoginActivity.this, newName, savedPass); - } - } catch (KeyStoreHelper.BrokenPasswordStoreException ex) { - Timber.w(ex); - } finally { - // we have either set a new password or it is broken - kill the old one either way - KeyStoreHelper.removeWalletUserPass(LoginActivity.this, oldName); - } - 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(); - } + private void renameWallet(String oldName, String newName) { + File walletFile = Helper.getWalletFile(this, oldName); + boolean success = renameWallet(walletFile, newName); + if (success) { + 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 + // copy + delete seems safer than rename because we can rollback easily boolean renameWallet(File walletFile, String newName) { if (copyWallet(walletFile, new File(walletFile.getParentFile(), newName), false, true)) { - deleteWallet(walletFile); + try { + KeyStoreHelper.copyWalletUserPass(this, walletFile.getName(), newName); + } catch (KeyStoreHelper.BrokenPasswordStoreException ex) { + Timber.w(ex); + } + deleteWallet(walletFile); // also deletes the keystore entry return true; } else { return false; @@ -468,154 +437,123 @@ public class LoginActivity extends BaseActivity 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); - } + (dialog, id) -> { + Helper.hideKeyboardAlways(LoginActivity.this); + String newName = etRename.getText().toString(); + renameWallet(walletName, newName); }) .setNegativeButton(getString(R.string.label_cancel), - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - Helper.hideKeyboardAlways(LoginActivity.this); - dialog.cancel(); - } + (dialog, 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) && (event.getAction() == KeyEvent.ACTION_DOWN)) - || (actionId == EditorInfo.IME_ACTION_DONE)) { - Helper.hideKeyboardAlways(LoginActivity.this); - String newName = etRename.getText().toString(); - dialog.cancel(); - new AsyncRename().execute(walletName, newName); - return false; - } + etRename.setOnEditorActionListener((v, actionId, event) -> { + if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN)) + || (actionId == EditorInfo.IME_ACTION_DONE)) { + Helper.hideKeyboardAlways(LoginActivity.this); + String newName = etRename.getText().toString(); + dialog.cancel(); + renameWallet(walletName, newName); return false; } + return false; }); dialog.show(); } - private class AsyncBackup extends AsyncTask<String, Void, Boolean> { - @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(); - } else { - Toast.makeText(LoginActivity.this, getString(R.string.backup_success), Toast.LENGTH_SHORT).show(); - } - } - } - - 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; - } + private static final int CREATE_BACKUP_INTENT = 4711; + private static final int RESTORE_BACKUP_INTENT = 4712; + private ZipBackup zipBackup; @Override public void onWalletBackup(String walletName) { Timber.d("backup for wallet ." + walletName + "."); - new AsyncBackup().execute(walletName); + zipBackup = new ZipBackup(this, walletName); + + Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("application/zip"); + intent.putExtra(Intent.EXTRA_TITLE, zipBackup.getBackupName()); + startActivityForResult(intent, CREATE_BACKUP_INTENT); } - private class AsyncArchive extends AsyncTask<String, Void, Boolean> { - @Override - protected void onPreExecute() { - super.onPreExecute(); - showProgressDialog(R.string.archive_progress); - } + @Override + public void onWalletRestore() { + Timber.d("restore wallet"); - @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; - } - } + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("application/zip"); + startActivityForResult(intent, RESTORE_BACKUP_INTENT); + } - @Override - protected void onPostExecute(Boolean result) { - super.onPostExecute(result); - if (isDestroyed()) { + @Override + protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == CREATE_BACKUP_INTENT) { + if (data == null) { + // nothing selected + Toast.makeText(this, getString(R.string.backup_failed), Toast.LENGTH_LONG).show(); return; } - dismissProgressDialog(); - if (result) { - reloadWalletList(); - } else { - Toast.makeText(LoginActivity.this, getString(R.string.archive_failed), Toast.LENGTH_LONG).show(); + try { + zipBackup.writeTo(data.getData()); + Toast.makeText(this, getString(R.string.backup_success), Toast.LENGTH_SHORT).show(); + } catch (IOException ex) { + Timber.e(ex); + Toast.makeText(this, getString(R.string.backup_failed), Toast.LENGTH_LONG).show(); + } + } else if (requestCode == RESTORE_BACKUP_INTENT) { + if (data == null) { + // nothing selected + Toast.makeText(this, getString(R.string.restore_failed), Toast.LENGTH_LONG).show(); + return; + } + try { + ZipRestore zipRestore = new ZipRestore(this, data.getData()); + Toast.makeText(this, getString(R.string.menu_restore), Toast.LENGTH_SHORT).show(); + if (zipRestore.restore()) { + reloadWalletList(); + } else { + Toast.makeText(this, getString(R.string.restore_failed), Toast.LENGTH_LONG).show(); + } + } catch (IOException ex) { + Timber.e(ex); + Toast.makeText(this, getString(R.string.restore_failed), Toast.LENGTH_LONG).show(); } } } @Override - public void onWalletArchive(final String walletName) { - Timber.d("archive for wallet ." + walletName + "."); + public void onWalletDelete(final String walletName) { + Timber.d("delete 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; - } + DialogInterface.OnClickListener dialogClickListener = (dialog, which) -> { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + if (deleteWallet(Helper.getWalletFile(LoginActivity.this, walletName))) { + reloadWalletList(); + } else { + Toast.makeText(LoginActivity.this, getString(R.string.delete_failed), Toast.LENGTH_LONG).show(); + } + break; + case DialogInterface.BUTTON_NEGATIVE: + // do nothing + break; } }; AlertDialog.Builder builder = new MaterialAlertDialogBuilder(this); - builder.setMessage(getString(R.string.archive_alert_message)) + builder.setMessage(getString(R.string.delete_alert_message)) .setTitle(walletName) - .setPositiveButton(getString(R.string.archive_alert_yes), dialogClickListener) - .setNegativeButton(getString(R.string.archive_alert_no), dialogClickListener) + .setPositiveButton(getString(R.string.delete_alert_yes), dialogClickListener) + .setNegativeButton(getString(R.string.delete_alert_no), dialogClickListener) .show(); } @@ -628,6 +566,7 @@ public class LoginActivity extends BaseActivity loginFragment.loadList(); } } catch (ClassCastException ex) { + Timber.w(ex); } } @@ -774,37 +713,6 @@ public class LoginActivity extends BaseActivity startReviewFragment(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() { // we set these here because we cannot be ceratin we have permissions for storage before Helper.setMoneroHome(this); @@ -1035,22 +943,12 @@ public class LoginActivity extends BaseActivity }); } - void toast(final String msg) { - runOnUiThread(new Runnable() { - @Override - public void run() { - Toast.makeText(LoginActivity.this, msg, Toast.LENGTH_LONG).show(); - } - }); + private void toast(final String msg) { + runOnUiThread(() -> Toast.makeText(LoginActivity.this, msg, Toast.LENGTH_LONG).show()); } - void toast(final int msgId) { - runOnUiThread(new Runnable() { - @Override - public void run() { - Toast.makeText(LoginActivity.this, getString(msgId), Toast.LENGTH_LONG).show(); - } - }); + private void toast(final int msgId) { + runOnUiThread(() -> Toast.makeText(LoginActivity.this, getString(msgId), Toast.LENGTH_LONG).show()); } @Override @@ -1058,7 +956,7 @@ public class LoginActivity extends BaseActivity 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 + walletFile.delete(); // when recovering wallets, the cache seems corrupt - so remove it popFragmentStack(GENERATE_STACK); Toast.makeText(LoginActivity.this, @@ -1079,8 +977,7 @@ public class LoginActivity extends BaseActivity } } - boolean copyWallet(File srcWallet, File dstWallet, boolean overwrite, - boolean ignoreCacheError) { + boolean copyWallet(File srcWallet, File dstWallet, boolean overwrite, boolean ignoreCacheError) { if (walletExists(dstWallet, true) && !overwrite) return false; boolean success = false; File srcDir = srcWallet.getParentFile(); @@ -1123,19 +1020,14 @@ public class LoginActivity extends BaseActivity success = addressFile.delete() && success; } Timber.d("deleteWallet is %s", success); + KeyStoreHelper.removeWalletUserPass(this, walletFile.getName()); return success; } void copyFile(File src, File dst) throws IOException { - FileChannel inChannel = new FileInputStream(src).getChannel(); - FileChannel outChannel = new FileOutputStream(dst).getChannel(); - try { + try (FileChannel inChannel = new FileInputStream(src).getChannel(); + FileChannel outChannel = new FileOutputStream(dst).getChannel()) { inChannel.transferTo(0, inChannel.size(), outChannel); - } finally { - if (inChannel != null) - inChannel.close(); - if (outChannel != null) - outChannel.close(); } } @@ -1220,63 +1112,64 @@ public class LoginActivity extends BaseActivity @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_create_help_ledger: - HelpFragment.display(getSupportFragmentManager(), R.string.help_create_ledger); - 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_help_node: - HelpFragment.display(getSupportFragmentManager(), R.string.help_node); - return true; - case R.id.action_default_nodes: { - Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container); - if ((WalletManager.getInstance().getNetworkType() == NetworkType.NetworkType_Mainnet) && - (f instanceof NodeFragment)) { - ((NodeFragment) f).restoreDefaultNodes(); - } - return true; + final int id = item.getItemId(); + if (id == R.id.action_create_help_new) { + HelpFragment.display(getSupportFragmentManager(), R.string.help_create_new); + return true; + } else if (id == R.id.action_create_help_keys) { + HelpFragment.display(getSupportFragmentManager(), R.string.help_create_keys); + return true; + } else if (id == R.id.action_create_help_view) { + HelpFragment.display(getSupportFragmentManager(), R.string.help_create_view); + return true; + } else if (id == R.id.action_create_help_seed) { + HelpFragment.display(getSupportFragmentManager(), R.string.help_create_seed); + return true; + } else if (id == R.id.action_create_help_ledger) { + HelpFragment.display(getSupportFragmentManager(), R.string.help_create_ledger); + return true; + } else if (id == R.id.action_details_help) { + HelpFragment.display(getSupportFragmentManager(), R.string.help_details); + return true; + } else if (id == R.id.action_details_changepw) { + onWalletChangePassword(); + return true; + } else if (id == R.id.action_license_info) { + AboutFragment.display(getSupportFragmentManager()); + return true; + } else if (id == R.id.action_help_list) { + HelpFragment.display(getSupportFragmentManager(), R.string.help_list); + return true; + } else if (id == R.id.action_help_node) { + HelpFragment.display(getSupportFragmentManager(), R.string.help_node); + return true; + } else if (id == R.id.action_default_nodes) { + Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container); + if ((WalletManager.getInstance().getNetworkType() == NetworkType.NetworkType_Mainnet) && + (f instanceof NodeFragment)) { + ((NodeFragment) f).restoreDefaultNodes(); } - case R.id.action_privacy_policy: - PrivacyFragment.display(getSupportFragmentManager()); - return true; - case R.id.action_language: - onChangeLocale(); - return true; - case R.id.action_theme: - onChangeTheme(); - return true; - case R.id.action_ledger_seed: { - Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container); - if (f instanceof GenerateFragment) { - ((GenerateFragment) f).convertLedgerSeed(); - } - return true; + return true; + } else if (id == R.id.action_privacy_policy) { + PrivacyFragment.display(getSupportFragmentManager()); + return true; + } else if (id == R.id.action_language) { + onChangeLocale(); + return true; + } else if (id == R.id.action_theme) { + onChangeTheme(); + return true; + } else if (id == R.id.action_restore) { + onWalletRestore(); + return true; + } else if (id == R.id.action_ledger_seed) { + Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container); + if (f instanceof GenerateFragment) { + ((GenerateFragment) f).convertLedgerSeed(); } - default: - return super.onOptionsItemSelected(item); + return true; + } else { + return super.onOptionsItemSelected(item); } } @@ -1317,7 +1210,7 @@ public class LoginActivity extends BaseActivity if (result) { Timber.d("selected wallet is .%s.", node.getName()); // now it's getting real, onValidateFields if wallet exists - promptAndStart(walletName, node, streetmode); + promptAndStart(walletName, streetmode); } else { if (node.getResponseCode() == 0) { // IOException Toast.makeText(LoginActivity.this, getString(R.string.status_wallet_node_invalid), Toast.LENGTH_LONG).show(); @@ -1333,25 +1226,21 @@ public class LoginActivity extends BaseActivity String keyPath = new File(Helper.getWalletRoot(LoginActivity.this), walletName + ".keys").getAbsolutePath(); // check if we need connected hardware - Wallet.Device device = - WalletManager.getInstance().queryWalletDevice(keyPath, password); - switch (device) { - case Device_Ledger: - if (!hasLedger()) { - toast(R.string.open_wallet_ledger_missing); - } else { - return true; - } - break; - default: - // device could be undefined meaning the password is wrong - // this gets dealt with later + Wallet.Device device = WalletManager.getInstance().queryWalletDevice(keyPath, password); + if (device == Wallet.Device.Device_Ledger) { + if (!hasLedger()) { + toast(R.string.open_wallet_ledger_missing); + } else { return true; + } + } else {// device could be undefined meaning the password is wrong + // this gets dealt with later + return true; } return false; } - void promptAndStart(String walletName, Node node, final boolean streetmode) { + void promptAndStart(String walletName, final boolean streetmode) { File walletFile = Helper.getWalletFile(this, walletName); if (WalletManager.getInstance().walletExists(walletFile)) { Helper.promptPassword(LoginActivity.this, walletName, false, @@ -1418,38 +1307,23 @@ public class LoginActivity extends BaseActivity Ledger.connect(usbManager, usbDevice); if (!Ledger.check()) { Ledger.disconnect(); - runOnUiThread(new Runnable() { - @Override - public void run() { - Toast.makeText(LoginActivity.this, - getString(R.string.toast_ledger_start_app, usbDevice.getProductName()), - Toast.LENGTH_SHORT) - .show(); - } - }); + runOnUiThread(() -> Toast.makeText(LoginActivity.this, + getString(R.string.toast_ledger_start_app, usbDevice.getProductName()), + Toast.LENGTH_SHORT) + .show()); } else { registerDetachReceiver(); onLedgerAction(); - runOnUiThread(new Runnable() { - @Override - public void run() { - Toast.makeText(LoginActivity.this, - getString(R.string.toast_ledger_attached, usbDevice.getProductName()), - Toast.LENGTH_SHORT) - .show(); - } - }); + runOnUiThread(() -> Toast.makeText(LoginActivity.this, + getString(R.string.toast_ledger_attached, usbDevice.getProductName()), + Toast.LENGTH_SHORT) + .show()); } } catch (IOException ex) { - runOnUiThread(new Runnable() { - @Override - public void run() { - Toast.makeText(LoginActivity.this, - getString(R.string.open_wallet_ledger_missing), - Toast.LENGTH_SHORT) - .show(); - } - }); + runOnUiThread(() -> Toast.makeText(LoginActivity.this, + getString(R.string.open_wallet_ledger_missing), + Toast.LENGTH_SHORT) + .show()); } } @@ -1507,15 +1381,10 @@ public class LoginActivity extends BaseActivity final UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); Timber.i("Ledger detached!"); if (device != null) - runOnUiThread(new Runnable() { - @Override - public void run() { - Toast.makeText(LoginActivity.this, - getString(R.string.toast_ledger_detached, device.getProductName()), - Toast.LENGTH_SHORT) - .show(); - } - }); + runOnUiThread(() -> Toast.makeText(LoginActivity.this, + getString(R.string.toast_ledger_detached, device.getProductName()), + Toast.LENGTH_SHORT) + .show()); Ledger.disconnect(); onLedgerAction(); } diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java index e6caa3fa..0db596ea 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java @@ -31,7 +31,6 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; -import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -62,8 +61,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter private WalletInfoAdapter adapter; - private List<WalletManager.WalletInfo> walletList = new ArrayList<>(); - private List<WalletManager.WalletInfo> displayedList = new ArrayList<>(); + private final List<WalletManager.WalletInfo> walletList = new ArrayList<>(); private View tvGuntherSays; private ImageView ivGunther; @@ -86,7 +84,9 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter void onWalletBackup(String name); - void onWalletArchive(String walletName); + void onWalletRestore(); + + void onWalletDelete(String walletName); void onAddWallet(String type); @@ -110,7 +110,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter } @Override - public void onAttach(Context context) { + public void onAttach(@NonNull Context context) { super.onAttach(context); if (context instanceof Listener) { this.activityCallback = (Listener) context; @@ -200,12 +200,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter // Wallet touched @Override public void onInteraction(final View view, final WalletManager.WalletInfo infoItem) { - String addressPrefix = WalletManager.getInstance().addressPrefix(); - if (addressPrefix.indexOf(infoItem.address.charAt(0)) < 0) { - Toast.makeText(getActivity(), getString(R.string.prompt_wrong_net), Toast.LENGTH_LONG).show(); - return; - } - openWallet(infoItem.name, false); + openWallet(infoItem.getName(), false); } private void openWallet(String name, boolean streetmode) { @@ -214,48 +209,32 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter @Override public boolean onContextInteraction(MenuItem item, WalletManager.WalletInfo listItem) { - switch (item.getItemId()) { - case R.id.action_streetmode: - openWallet(listItem.name, true); - break; - case R.id.action_info: - showInfo(listItem.name); - break; - case R.id.action_rename: - activityCallback.onWalletRename(listItem.name); - break; - case R.id.action_backup: - activityCallback.onWalletBackup(listItem.name); - break; - case R.id.action_archive: - activityCallback.onWalletArchive(listItem.name); - break; - default: - return super.onContextItemSelected(item); + final int id = item.getItemId(); + if (id == R.id.action_streetmode) { + openWallet(listItem.getName(), true); + } else if (id == R.id.action_info) { + showInfo(listItem.getName()); + } else if (id == R.id.action_rename) { + activityCallback.onWalletRename(listItem.getName()); + } else if (id == R.id.action_backup) { + activityCallback.onWalletBackup(listItem.getName()); + } else if (id == R.id.action_archive) { + activityCallback.onWalletDelete(listItem.getName()); + } else { + return super.onContextItemSelected(item); } return true; } - private void filterList() { - displayedList.clear(); - String addressPrefix = WalletManager.getInstance().addressPrefix(); - for (WalletManager.WalletInfo s : walletList) { - if (addressPrefix.indexOf(s.address.charAt(0)) >= 0) displayedList.add(s); - } - } - public void loadList() { Timber.d("loadList()"); WalletManager mgr = WalletManager.getInstance(); - List<WalletManager.WalletInfo> walletInfos = - mgr.findWallets(activityCallback.getStorageRoot()); walletList.clear(); - walletList.addAll(walletInfos); - filterList(); - adapter.setInfos(displayedList); + walletList.addAll(mgr.findWallets(activityCallback.getStorageRoot())); + adapter.setInfos(walletList); // deal with Gunther & FAB animation - if (displayedList.isEmpty()) { + if (walletList.isEmpty()) { fab.startAnimation(fab_pulse); if (ivGunther.getDrawable() == null) { ivGunther.setImageResource(R.drawable.ic_emptygunther); @@ -274,7 +253,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter .getSharedPreferences(KeyStoreHelper.SecurityConstants.WALLET_PASS_PREFS_NAME, Context.MODE_PRIVATE) .getAll().keySet(); for (WalletManager.WalletInfo s : walletList) { - removedWallets.remove(s.name); + removedWallets.remove(s.getName()); } for (String name : removedWallets) { KeyStoreHelper.removeWalletUserPass(getActivity(), name); @@ -292,7 +271,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter } @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.list_menu, menu); super.onCreateOptionsMenu(menu, inflater); } @@ -362,37 +341,29 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter @Override public void onClick(View v) { - int id = v.getId(); + final int id = v.getId(); Timber.d("onClick %d/%d", id, R.id.fabLedger); - switch (id) { - case R.id.fab: - animateFAB(); - break; - case R.id.fabNew: - fabScreen.setVisibility(View.INVISIBLE); - isFabOpen = false; - activityCallback.onAddWallet(GenerateFragment.TYPE_NEW); - break; - case R.id.fabView: - animateFAB(); - activityCallback.onAddWallet(GenerateFragment.TYPE_VIEWONLY); - break; - case R.id.fabKey: - animateFAB(); - activityCallback.onAddWallet(GenerateFragment.TYPE_KEY); - break; - case R.id.fabSeed: - animateFAB(); - activityCallback.onAddWallet(GenerateFragment.TYPE_SEED); - break; - case R.id.fabLedger: - Timber.d("FAB_LEDGER"); - animateFAB(); - activityCallback.onAddWallet(GenerateFragment.TYPE_LEDGER); - break; - case R.id.fabScreen: - animateFAB(); - break; + if (id == R.id.fab) { + animateFAB(); + } else if (id == R.id.fabNew) { + fabScreen.setVisibility(View.INVISIBLE); + isFabOpen = false; + activityCallback.onAddWallet(GenerateFragment.TYPE_NEW); + } else if (id == R.id.fabView) { + animateFAB(); + activityCallback.onAddWallet(GenerateFragment.TYPE_VIEWONLY); + } else if (id == R.id.fabKey) { + animateFAB(); + activityCallback.onAddWallet(GenerateFragment.TYPE_KEY); + } else if (id == R.id.fabSeed) { + animateFAB(); + activityCallback.onAddWallet(GenerateFragment.TYPE_SEED); + } else if (id == R.id.fabLedger) { + Timber.d("FAB_LEDGER"); + animateFAB(); + activityCallback.onAddWallet(GenerateFragment.TYPE_LEDGER); + } else if (id == R.id.fabScreen) { + animateFAB(); } } diff --git a/app/src/main/java/com/m2049r/xmrwallet/layout/WalletInfoAdapter.java b/app/src/main/java/com/m2049r/xmrwallet/layout/WalletInfoAdapter.java index b9ad73c9..ad885e76 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/layout/WalletInfoAdapter.java +++ b/app/src/main/java/com/m2049r/xmrwallet/layout/WalletInfoAdapter.java @@ -74,7 +74,7 @@ public class WalletInfoAdapter extends RecyclerView.Adapter<WalletInfoAdapter.Vi @Override public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { - return mOldList.get(oldItemPosition).name.equals(mNewList.get(newItemPosition).name); + return mOldList.get(oldItemPosition).getName().equals(mNewList.get(newItemPosition).getName()); } @Override @@ -86,9 +86,9 @@ public class WalletInfoAdapter extends RecyclerView.Adapter<WalletInfoAdapter.Vi @NonNull @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - View view = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_wallet, parent, false); - return new ViewHolder(view); + return new ViewHolder( + LayoutInflater.from(parent.getContext()).inflate(R.layout.item_wallet, parent, false) + ); } @Override @@ -122,7 +122,6 @@ public class WalletInfoAdapter extends RecyclerView.Adapter<WalletInfoAdapter.Vi class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { final TextView tvName; - final TextView tvAddress; final ImageButton ibOptions; WalletManager.WalletInfo infoItem; boolean popupOpen = false; @@ -130,7 +129,6 @@ public class WalletInfoAdapter extends RecyclerView.Adapter<WalletInfoAdapter.Vi ViewHolder(View itemView) { super(itemView); tvName = itemView.findViewById(R.id.tvName); - tvAddress = itemView.findViewById(R.id.tvAddress); ibOptions = itemView.findViewById(R.id.ibOptions); ibOptions.setOnClickListener(view -> { if (popupOpen) return; @@ -160,8 +158,7 @@ public class WalletInfoAdapter extends RecyclerView.Adapter<WalletInfoAdapter.Vi void bind(int position) { infoItem = infoItems.get(position); - tvName.setText(infoItem.name); - tvAddress.setText(infoItem.address.substring(0, 16) + "..."); + tvName.setText(infoItem.getName()); } @Override diff --git a/app/src/main/java/com/m2049r/xmrwallet/model/WalletManager.java b/app/src/main/java/com/m2049r/xmrwallet/model/WalletManager.java index 2fffcae1..2a6ffd0d 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/model/WalletManager.java +++ b/app/src/main/java/com/m2049r/xmrwallet/model/WalletManager.java @@ -21,15 +21,13 @@ import com.m2049r.xmrwallet.data.Node; import com.m2049r.xmrwallet.ledger.Ledger; import com.m2049r.xmrwallet.util.RestoreHeight; -import java.io.BufferedReader; import java.io.File; -import java.io.FileReader; import java.io.FilenameFilter; -import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.List; +import lombok.Getter; import timber.log.Timber; public class WalletManager { @@ -103,7 +101,8 @@ public class WalletManager { wallet.setRestoreHeight(restoreHeight); Timber.d("Changed Restore Height from %d to %d", oldHeight, wallet.getRestoreHeight()); wallet.setPassword(password); // this rewrites the keys file (which contains the restore height) - } + } else + Timber.e(wallet.getStatus().toString()); return wallet; } @@ -210,46 +209,22 @@ public class WalletManager { //public native List<String> findWallets(String path); // this does not work - some error in boost public class WalletInfo implements Comparable<WalletInfo> { - public File path; - public String name; - public String address; + @Getter + final private File path; + @Getter + final private String name; + + public WalletInfo(File wallet) { + path = wallet.getParentFile(); + name = wallet.getName(); + } @Override public int compareTo(WalletInfo another) { - int n = name.toLowerCase().compareTo(another.name.toLowerCase()); - if (n != 0) { - return n; - } else { // wallet names are the same - return address.compareTo(another.address); - } + return name.toLowerCase().compareTo(another.name.toLowerCase()); } } - public WalletInfo getWalletInfo(File wallet) { - WalletInfo info = new WalletInfo(); - info.path = wallet.getParentFile(); - info.name = wallet.getName(); - File addressFile = new File(info.path, info.name + ".address.txt"); - //Timber.d(addressFile.getAbsolutePath()); - info.address = "??????"; - BufferedReader addressReader = null; - try { - addressReader = new BufferedReader(new FileReader(addressFile)); - info.address = addressReader.readLine(); - } catch (IOException ex) { - Timber.d(ex.getLocalizedMessage()); - } finally { - if (addressReader != null) { - try { - addressReader.close(); - } catch (IOException ex) { - // that's just too bad - } - } - } - return info; - } - public List<WalletInfo> findWallets(File path) { List<WalletInfo> wallets = new ArrayList<>(); Timber.d("Scanning: %s", path.getAbsolutePath()); @@ -261,7 +236,7 @@ public class WalletManager { for (int i = 0; i < found.length; i++) { String filename = found[i].getName(); File f = new File(found[i].getParent(), filename.substring(0, filename.length() - 5)); // 5 is length of ".keys"+1 - wallets.add(getWalletInfo(f)); + wallets.add(new WalletInfo(f)); } return wallets; } diff --git a/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java b/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java index 2428cb35..e500e8cc 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java +++ b/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java @@ -34,7 +34,6 @@ import android.hardware.fingerprint.FingerprintManager; import android.os.AsyncTask; import android.os.Build; import android.os.CancellationSignal; -import android.os.Environment; import android.os.StrictMode; import android.system.ErrnoException; import android.system.Os; @@ -77,16 +76,12 @@ import javax.net.ssl.HttpsURLConnection; import timber.log.Timber; public class Helper { - static private final String FLAVOR_SUFFIX = - (BuildConfig.FLAVOR.startsWith("prod") ? "" : "." + BuildConfig.FLAVOR) - + (BuildConfig.DEBUG ? "-debug" : ""); - static public final String NOCRAZYPASS_FLAGFILE = ".nocrazypass"; static public final String BASE_CRYPTO = "XMR"; - static private final String WALLET_DIR = "monerujo" + FLAVOR_SUFFIX; - static private final String HOME_DIR = "monero" + FLAVOR_SUFFIX; + static private final String WALLET_DIR = "wallets"; + static private final String MONERO_DIR = "monero"; static public int DISPLAY_DIGITS_INFO = 5; @@ -95,12 +90,7 @@ public class Helper { } static public File getStorage(Context context, String folderName) { - if (!isExternalStorageWritable()) { - String msg = context.getString(R.string.message_strorage_not_writable); - Timber.e(msg); - throw new IllegalStateException(msg); - } - File dir = new File(Environment.getExternalStorageDirectory(), folderName); + File dir = new File(context.getFilesDir(), folderName); if (!dir.exists()) { Timber.i("Creating %s", dir.getAbsolutePath()); dir.mkdirs(); // try to make it @@ -113,24 +103,6 @@ public class Helper { return dir; } - static public final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1; - - static public boolean getWritePermission(Activity context) { - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { - if (context.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) - == PackageManager.PERMISSION_DENIED) { - Timber.w("Permission denied to WRITE_EXTERNAL_STORAGE - requesting it"); - String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE}; - context.requestPermissions(permissions, PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE); - return false; - } else { - return true; - } - } else { - return true; - } - } - static public final int PERMISSIONS_REQUEST_CAMERA = 7; static public boolean getCameraPermission(Activity context) { @@ -156,12 +128,6 @@ public class Helper { return f; } - /* Checks if external storage is available for read and write */ - private static boolean isExternalStorageWritable() { - String state = Environment.getExternalStorageState(); - return Environment.MEDIA_MOUNTED.equals(state); - } - static public void showKeyboard(Activity act) { InputMethodManager imm = (InputMethodManager) act.getSystemService(Context.INPUT_METHOD_SERVICE); final View focus = act.getCurrentFocus(); @@ -339,7 +305,7 @@ public class Helper { static public void setMoneroHome(Context context) { try { - String home = getStorage(context, HOME_DIR).getAbsolutePath(); + String home = getStorage(context, MONERO_DIR).getAbsolutePath(); Os.setenv("HOME", home, true); } catch (ErrnoException ex) { throw new IllegalStateException(ex); @@ -355,7 +321,7 @@ public class Helper { // TODO make the log levels refer to the WalletManagerFactory::LogLevel enum ? static public void initLogger(Context context, int level) { - String home = getStorage(context, HOME_DIR).getAbsolutePath(); + String home = getStorage(context, MONERO_DIR).getAbsolutePath(); WalletManager.initLogger(home + "/monerujo", "monerujo.log"); if (level >= WalletManager.LOGLEVEL_SILENT) WalletManager.setLogLevel(level); diff --git a/app/src/main/java/com/m2049r/xmrwallet/util/KeyStoreHelper.java b/app/src/main/java/com/m2049r/xmrwallet/util/KeyStoreHelper.java index 7c08f458..896d2d02 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/util/KeyStoreHelper.java +++ b/app/src/main/java/com/m2049r/xmrwallet/util/KeyStoreHelper.java @@ -24,9 +24,10 @@ import android.os.Build; import android.security.KeyPairGeneratorSpec; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; -import androidx.annotation.NonNull; import android.util.Base64; +import androidx.annotation.NonNull; + import java.io.IOException; import java.math.BigInteger; import java.nio.charset.StandardCharsets; @@ -167,6 +168,11 @@ public class KeyStoreHelper { .remove(wallet).apply(); } + public static void copyWalletUserPass(Context context, String srcWallet, String dstWallet) throws BrokenPasswordStoreException { + final String pass = loadWalletUserPass(context, srcWallet); + saveWalletUserPass(context, dstWallet, pass); + } + /** * Creates a public and private key and stores it using the Android Key * Store, so that only this application will be able to access the keys. diff --git a/app/src/main/java/com/m2049r/xmrwallet/util/LegacyStorageHelper.java b/app/src/main/java/com/m2049r/xmrwallet/util/LegacyStorageHelper.java new file mode 100644 index 00000000..60bbf4e2 --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/util/LegacyStorageHelper.java @@ -0,0 +1,170 @@ +package com.m2049r.xmrwallet.util; + +import android.Manifest; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Environment; +import android.preference.PreferenceManager; + +import com.m2049r.xmrwallet.BuildConfig; +import com.m2049r.xmrwallet.model.WalletManager; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import lombok.RequiredArgsConstructor; +import timber.log.Timber; + +@RequiredArgsConstructor +public class LegacyStorageHelper { + final private File srcDir; + final private File dstDir; + + static public void migrateWallets(Context context) { + try { + if (isStorageMigrated(context)) return; + if (!hasWritePermission(context)) { + // nothing to migrate, so don't try again + setStorageMigrated(context); + return; + } + final File oldRoot = getWalletRoot(); + if (!oldRoot.exists()) { + // nothing to migrate, so don't try again + setStorageMigrated(context); + return; + } + final File newRoot = Helper.getWalletRoot(context); + (new LegacyStorageHelper(oldRoot, newRoot)).migrate(); + setStorageMigrated(context); // done it once - don't try again + } catch (IllegalStateException ex) { + Timber.d(ex); + // nothing we can do here + } + } + + public void migrate() { + String addressPrefix = WalletManager.getInstance().addressPrefix(); + File[] wallets = srcDir.listFiles((dir, filename) -> filename.endsWith(".keys")); + for (File wallet : wallets) { + final String walletName = wallet.getName().substring(0, wallet.getName().length() - ".keys".length()); + if (addressPrefix.indexOf(getAddress(walletName).charAt(0)) < 0) { + Timber.d("skipping %s", walletName); + continue; + } + try { + copy(walletName); + } catch (IOException ex) { // something failed - try to clean up + deleteDst(walletName); + } + } + } + + // return "@" by default so we don't need to deal with null stuff + private String getAddress(String walletName) { + File addressFile = new File(srcDir, walletName + ".address.txt"); + if (!addressFile.exists()) return "@"; + try (BufferedReader addressReader = new BufferedReader(new FileReader(addressFile))) { + return addressReader.readLine(); + } catch (IOException ex) { + Timber.d(ex.getLocalizedMessage()); + } + return "@"; + } + + private void copy(String walletName) throws IOException { + final String dstName = getUniqueName(dstDir, walletName); + copyFile(new File(srcDir, walletName), new File(dstDir, dstName)); + copyFile(new File(srcDir, walletName + ".keys"), new File(dstDir, dstName + ".keys")); + } + + private void deleteDst(String walletName) { + // do our best, but if it fails, it fails + (new File(dstDir, walletName)).delete(); + (new File(dstDir, walletName + ".keys")).delete(); + } + + private void copyFile(File src, File dst) throws IOException { + if (!src.exists()) return; + Timber.d("%s => %s", src.getAbsolutePath(), dst.getAbsolutePath()); + try (FileChannel inChannel = new FileInputStream(src).getChannel(); + FileChannel outChannel = new FileOutputStream(dst).getChannel()) { + inChannel.transferTo(0, inChannel.size(), outChannel); + } + } + + private static boolean isExternalStorageWritable() { + String state = Environment.getExternalStorageState(); + return Environment.MEDIA_MOUNTED.equals(state); + } + + private static File getWalletRoot() { + if (!isExternalStorageWritable()) + throw new IllegalStateException(); + + // wallet folder for legacy (pre-Q) installations + final String FLAVOR_SUFFIX = + (BuildConfig.FLAVOR.startsWith("prod") ? "" : "." + BuildConfig.FLAVOR) + + (BuildConfig.DEBUG ? "-debug" : ""); + final String WALLET_DIR = "monerujo" + FLAVOR_SUFFIX; + + File dir = new File(Environment.getExternalStorageDirectory(), WALLET_DIR); + if (!dir.exists() || !dir.isDirectory()) + throw new IllegalStateException(); + return dir; + } + + private static boolean hasWritePermission(Context context) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + return context.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_DENIED; + } else { + return true; + } + } + + private static final Pattern WALLET_PATTERN = Pattern.compile("^(.+) \\(([0-9]+)\\).keys$"); + + private static String getUniqueName(File root, String name) { + if (!(new File(root, name + ".keys")).exists()) // <name> does not exist => it's ok to use + return name; + + File[] wallets = root.listFiles( + (dir, filename) -> { + Matcher m = WALLET_PATTERN.matcher(filename); + if (m.find()) + return m.group(1).equals(name); + else return false; + }); + if (wallets.length == 0) return name + " (1)"; + int maxIndex = 0; + for (File wallet : wallets) { + try { + final Matcher m = WALLET_PATTERN.matcher(wallet.getName()); + if (!m.find()) + throw new IllegalStateException("this must match as it did before"); + final int index = Integer.parseInt(m.group(2)); + if (index > maxIndex) maxIndex = index; + } catch (NumberFormatException ex) { + // this cannot happen & we can ignore it if it does + } + } + return name + " (" + (maxIndex + 1) + ")"; + } + + private static final String MIGRATED_KEY = "migrated_legacy_storage"; + + public static boolean isStorageMigrated(Context context) { + return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(MIGRATED_KEY, false); + } + + public static void setStorageMigrated(Context context) { + PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(MIGRATED_KEY, true).apply(); + } +} diff --git a/app/src/main/java/com/m2049r/xmrwallet/util/ZipBackup.java b/app/src/main/java/com/m2049r/xmrwallet/util/ZipBackup.java new file mode 100644 index 00000000..cfdc7052 --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/util/ZipBackup.java @@ -0,0 +1,64 @@ +package com.m2049r.xmrwallet.util; + +import android.content.Context; +import android.net.Uri; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class ZipBackup { + final private Context context; + final private String walletName; + + private ZipOutputStream zip; + + public void writeTo(Uri zipUri) throws IOException { + if (zip != null) + throw new IllegalStateException("zip already initialized"); + try { + zip = new ZipOutputStream(context.getContentResolver().openOutputStream(zipUri)); + + final File walletRoot = Helper.getWalletRoot(context); + addFile(new File(walletRoot, walletName + ".keys")); + addFile(new File(walletRoot, walletName)); + + zip.close(); + } finally { + if (zip != null) zip.close(); + } + } + + private void addFile(File file) throws IOException { + if (!file.exists()) return; // ignore missing files (e.g. the cache file might not exist) + ZipEntry entry = new ZipEntry(file.getName()); + zip.putNextEntry(entry); + writeFile(file); + zip.closeEntry(); + } + + private void writeFile(File source) throws IOException { + try (InputStream is = new FileInputStream(source)) { + byte[] buffer = new byte[8192]; + int length; + while ((length = is.read(buffer)) > 0) { + zip.write(buffer, 0, length); + } + } + } + + private static final SimpleDateFormat DATETIME_FORMATTER = + new SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss"); + + public String getBackupName() { + return walletName + " " + DATETIME_FORMATTER.format(new Date()); + } +} diff --git a/app/src/main/java/com/m2049r/xmrwallet/util/ZipRestore.java b/app/src/main/java/com/m2049r/xmrwallet/util/ZipRestore.java new file mode 100644 index 00000000..1b9148b2 --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/util/ZipRestore.java @@ -0,0 +1,139 @@ +package com.m2049r.xmrwallet.util; + +import android.content.Context; +import android.net.Uri; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import lombok.RequiredArgsConstructor; +import timber.log.Timber; + +@RequiredArgsConstructor +public class ZipRestore { + final private Context context; + final private Uri zipUri; + + private File walletRoot; + + private ZipInputStream zip; + + public boolean restore() throws IOException { + walletRoot = Helper.getWalletRoot(context); + String walletName = testArchive(); + if (walletName == null) return false; + + walletName = getUniqueName(walletName); + + if (zip != null) + throw new IllegalStateException("zip already initialized"); + try { + zip = new ZipInputStream(context.getContentResolver().openInputStream(zipUri)); + for (ZipEntry entry = zip.getNextEntry(); entry != null; zip.closeEntry(), entry = zip.getNextEntry()) { + File destination; + final String name = entry.getName(); + if (name.endsWith(".keys")) { + destination = new File(walletRoot, walletName + ".keys"); + } else if (name.endsWith(".address.txt")) { + destination = new File(walletRoot, walletName + ".address.txt"); + } else { + destination = new File(walletRoot, walletName); + } + writeFile(destination); + } + } finally { + if (zip != null) zip.close(); + } + return true; + } + + private void writeFile(File destination) throws IOException { + try (OutputStream os = new FileOutputStream(destination)) { + byte[] buffer = new byte[8192]; + int length; + while ((length = zip.read(buffer)) > 0) { + os.write(buffer, 0, length); + } + } + } + + // test the archive to contain files we expect & return the name of the contained wallet or null + private String testArchive() { + String walletName = null; + boolean keys = false; + ZipInputStream zipStream = null; + try { + zipStream = new ZipInputStream(context.getContentResolver().openInputStream(zipUri)); + for (ZipEntry entry = zipStream.getNextEntry(); entry != null; + zipStream.closeEntry(), entry = zipStream.getNextEntry()) { + if (entry.isDirectory()) + return null; + final String name = entry.getName(); + if ((new File(name)).getParentFile() != null) + return null; + if (walletName == null) { + if (name.endsWith(".keys")) { + walletName = name.substring(0, name.length() - ".keys".length()); + keys = true; // we have they keys + } else if (name.endsWith(".address.txt")) { + walletName = name.substring(0, name.length() - ".address.txt".length()); + } else { + walletName = name; + } + } else { // we have a wallet name + if (name.endsWith(".keys")) { + if (!name.equals(walletName + ".keys")) return null; + keys = true; // we have they keys + } else if (name.endsWith(".address.txt")) { + if (!name.equals(walletName + ".address.txt")) return null; + } else if (!name.equals(walletName)) return null; + } + } + } catch (IOException ex) { + return null; + } finally { + try { + if (zipStream != null) zipStream.close(); + } catch (IOException ex) { + Timber.w(ex); + } + } + // we need the keys at least + if (keys) return walletName; + else return null; + } + + final static Pattern WALLET_PATTERN = Pattern.compile("^(.+) \\(([0-9]+)\\).keys$"); + + private String getUniqueName(String name) { + if (!(new File(walletRoot, name + ".keys")).exists()) // <name> does not exist => it's ok to use + return name; + + File[] wallets = walletRoot.listFiles( + (dir, filename) -> { + Matcher m = WALLET_PATTERN.matcher(filename); + if (m.find()) + return m.group(1).equals(name); + else return false; + }); + if (wallets.length == 0) return name + " (1)"; + int maxIndex = 0; + for (File wallet : wallets) { + try { + final Matcher m = WALLET_PATTERN.matcher(wallet.getName()); + m.find(); + final int index = Integer.parseInt(m.group(2)); + if (index > maxIndex) maxIndex = index; + } catch (NumberFormatException ex) { + // this cannot happen & we can ignore it if it does + } + } + return name + " (" + (maxIndex + 1) + ")"; + } +} diff --git a/app/src/main/res/layout/item_wallet.xml b/app/src/main/res/layout/item_wallet.xml index 30e39275..0b918a14 100644 --- a/app/src/main/res/layout/item_wallet.xml +++ b/app/src/main/res/layout/item_wallet.xml @@ -18,19 +18,9 @@ android:layout_height="wrap_content" android:layout_marginStart="48dp" android:layout_marginTop="16dp" - android:layout_toStartOf="@+id/ibOptions" - tools:text="My Wallet" /> - - <TextView - android:id="@+id/tvAddress" - style="@style/MoneroText.Small" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@id/tvName" - android:layout_marginStart="48dp" android:layout_marginBottom="16dp" android:layout_toStartOf="@+id/ibOptions" - tools:text="4AdkPJox...g3viqCuk" /> + tools:text="My Wallet" /> <ImageButton android:id="@+id/ibOptions" diff --git a/app/src/main/res/menu/list_context_menu.xml b/app/src/main/res/menu/list_context_menu.xml index c43925c7..fda24cec 100644 --- a/app/src/main/res/menu/list_context_menu.xml +++ b/app/src/main/res/menu/list_context_menu.xml @@ -19,7 +19,7 @@ <item android:id="@+id/action_archive" android:orderInCategory="400" - android:title="@string/menu_archive" /> + android:title="@string/menu_delete" /> <item android:id="@+id/action_info" diff --git a/app/src/main/res/menu/list_menu.xml b/app/src/main/res/menu/list_menu.xml index 91f6b6ad..33e2f6b6 100644 --- a/app/src/main/res/menu/list_menu.xml +++ b/app/src/main/res/menu/list_menu.xml @@ -33,4 +33,9 @@ android:title="@string/menu_daynight" app:showAsAction="never" /> + <item + android:id="@+id/action_restore" + android:orderInCategory="600" + android:title="@string/menu_restore" + app:showAsAction="never" /> </menu> \ No newline at end of file diff --git a/app/src/main/res/values-cat/strings.xml b/app/src/main/res/values-cat/strings.xml index f3240ecd..6c517c6a 100644 --- a/app/src/main/res/values-cat/strings.xml +++ b/app/src/main/res/values-cat/strings.xml @@ -9,7 +9,6 @@ <string name="menu_help">Ajuda</string> <string name="menu_receive">Rebre</string> <string name="menu_rename">Canvi de nom</string> - <string name="menu_archive">Arxivar</string> <string name="menu_backup">Còpia de seguretat</string> <string name="menu_changepw">Canvi de contrasenya</string> @@ -94,7 +93,6 @@ <string name="backup_success">Còpia de seguretat amb èxit</string> <string name="backup_failed">Còpia de seguretat fallida!</string> - <string name="archive_failed">Arxiu fallit!</string> <string name="rename_failed">Canvi de nom fallit!</string> <string name="changepw_failed">Canvi de contrasenya fallit!</string> <string name="changepw_success">La contrasenya ha canviat</string> @@ -273,9 +271,8 @@ <string name="details_alert_no">Torna\'m enrere!</string> <string name="details_title">Detalls</string> - <string name="archive_alert_message">Es farà una còpia de seguretat del portamonedes i s\'esborrarà.</string> - <string name="archive_alert_yes">Si, fes-ho!</string> - <string name="archive_alert_no">No gràcies!</string> + <string name="delete_alert_yes">Si, fes-ho!</string> + <string name="delete_alert_no">No gràcies!</string> <string name="fab_create_new">Crear un nou portamonedes</string> <string name="fab_restore_viewonly">Restablir portamonedes de només lectura</string> @@ -420,4 +417,10 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="delete_alert_message">The wallet will be deleted!</string> + <string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> + <string name="delete_failed">Delete failed!</string> + + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 3b90f307..851b765b 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -9,7 +9,7 @@ <string name="menu_help">Hilfe</string> <string name="menu_receive">Empfangen</string> <string name="menu_rename">Umbenennen</string> - <string name="menu_archive">Archivieren</string> + <string name="menu_delete">Löschen</string> <string name="menu_backup">Backup</string> <string name="menu_changepw">Passwort ändern</string> @@ -94,7 +94,7 @@ <string name="backup_success">Backup erfolgreich</string> <string name="backup_failed">Backup fehlgeschlagen!</string> - <string name="archive_failed">Archivierung fehlgeschlagen!</string> + <string name="delete_failed">Löschen fehlgeschlagen!</string> <string name="rename_failed">Umbenennen fehlgeschlagen!</string> <string name="changepw_failed">Passwort ändern fehlgeschlagen!</string> <string name="changepw_success">Passwort geändert</string> @@ -266,9 +266,9 @@ <string name="details_alert_no">Nein, doch nicht!</string> <string name="details_title">Details</string> - <string name="archive_alert_message">Das Wallet wird archiviert und dann gelöscht!</string> - <string name="archive_alert_yes">Ja, mach das!</string> - <string name="archive_alert_no">Nein, danke!</string> + <string name="delete_alert_message">Das Wallet wird gelöscht!</string> + <string name="delete_alert_yes">Ja, mach das!</string> + <string name="delete_alert_no">Nein, danke!</string> <string name="fab_create_new">Neues Wallet erstellen</string> <string name="fab_restore_viewonly">View-Only-Wallet wiederherstellen</string> @@ -422,4 +422,6 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index ee14b0cc..34c692a7 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -9,7 +9,6 @@ <string name="menu_help">Βοήθεια</string> <string name="menu_receive">Λήψη</string> <string name="menu_rename">Μετονομασία</string> - <string name="menu_archive">Αρχειοθέτησε</string> <string name="menu_backup">Δημιουργία αντίγραφου ασφαλείας</string> <string name="password_weak">Συνέχισε να πληκτρολογείς …</string> @@ -92,7 +91,6 @@ <string name="backup_success">Η δημιουργία αντίγραφου ασφαλείας ήταν επιτυχής</string> <string name="backup_failed">Η δημιουργία αντίγραφου ασφαλείας απέτυχε!</string> - <string name="archive_failed">Η αρχειοθέτηση απέτυχε!</string> <string name="rename_failed">Η μετονομασία απέτυχε!</string> <string name="label_daemon">Κόμβος(Δαίμονας)</string> @@ -241,9 +239,8 @@ <string name="details_alert_no">Πήγαινε με πίσω!</string> <string name="details_title">Λεπτομέρειες</string> - <string name="archive_alert_message">Το πορτοφόλι θα δημιουργήσει αντίγραφο ασφαλείας και στη συνέχεια θα διαγραφεί!</string> - <string name="archive_alert_yes">Ναι, κάνε αυτό!</string> - <string name="archive_alert_no">Όχι ευχαριστώ!</string> + <string name="delete_alert_yes">Ναι, κάνε αυτό!</string> + <string name="delete_alert_no">Όχι ευχαριστώ!</string> <string name="fab_create_new">Δημιουργία νέου πορτοφολιού</string> <string name="fab_restore_viewonly">Επαναφορά πορτοφολιού προβολής-μόνο</string> @@ -422,4 +419,10 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="delete_alert_message">The wallet will be deleted!</string> + <string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> + <string name="delete_failed">Delete failed!</string> + + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml index df997955..6237dcf0 100644 --- a/app/src/main/res/values-eo/strings.xml +++ b/app/src/main/res/values-eo/strings.xml @@ -9,7 +9,6 @@ <string name="menu_help">Helpo</string> <string name="menu_receive">Ricevi</string> <string name="menu_rename">Renomi</string> - <string name="menu_archive">Arkivo</string> <string name="menu_backup">Sekurkopio</string> <string name="menu_changepw">Ŝanĝi pasfrazon</string> @@ -94,7 +93,6 @@ <string name="backup_success">Sekurkopio sukcesis!</string> <string name="backup_failed">Sekurkopio malsukcesis!</string> - <string name="archive_failed">Arkivado malsukcesis!</string> <string name="rename_failed">Renomado malsukcesis!</string> <string name="changepw_failed">Pasvortŝanĝo malsukcesis!</string> <string name="changepw_success">Pasvortŝanĝo sukcesis!</string> @@ -273,9 +271,8 @@ <string name="details_alert_no">Malantaŭen!</string> <string name="details_title">Detaloj</string> - <string name="archive_alert_message">La monujo sekurkopiĝos kaj poste forviŝiĝos!</string> - <string name="archive_alert_yes">Jes, faru tion!</string> - <string name="archive_alert_no">Ne dankon!</string> + <string name="delete_alert_yes">Jes, faru tion!</string> + <string name="delete_alert_no">Ne dankon!</string> <string name="fab_create_new">Krei novan monujon</string> <string name="fab_restore_viewonly">Restaŭri nurvidan monujon</string> @@ -422,4 +419,10 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="delete_alert_message">The wallet will be deleted!</string> + <string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> + <string name="delete_failed">Delete failed!</string> + + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 84f96d42..7023abe9 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -9,7 +9,6 @@ <string name="menu_help">Ayuda</string> <string name="menu_receive">Recibir</string> <string name="menu_rename">Renombrar</string> - <string name="menu_archive">Archivar</string> <string name="menu_backup">Copia de seguridad</string> <string name="menu_changepw">Cambiar contraseña</string> @@ -49,7 +48,6 @@ <string name="backup_success">Copia de seguridad exitosa</string> <string name="backup_failed">¡Copia de seguridad fallida!</string> - <string name="archive_failed">¡Archivado fallido!</string> <string name="rename_failed">¡Cambio de nombre fallido!</string> <string name="changepw_failed">¡Cambio de contraseña fallido!</string> <string name="changepw_success">Contraseña cambiada</string> @@ -209,9 +207,9 @@ <string name="details_alert_no">¡Llévame de vuelta!</string> <string name="details_title">Detalles</string> - <string name="archive_alert_message">¡El monedero será copiado y después borrado!</string> - <string name="archive_alert_yes">¡Sí, hazlo!</string> - <string name="archive_alert_no">¡No, gracias!</string> + <string name="delete_alert_message">¡El monedero será borrado!</string> + <string name="delete_alert_yes">¡Sí, hazlo!</string> + <string name="delete_alert_no">¡No, gracias!</string> <string name="fab_create_new">Crear nuevo monedero</string> <string name="fab_restore_viewonly">Restaurar monedero de sólo vista</string> @@ -413,4 +411,9 @@ <string name="subaddress_select_label">Elige una subdirección</string> <string name="subaddress_details_hint">Presiona largo para ver detalles</string> + <string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> + <string name="delete_failed">Delete failed!</string> + + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index 02e50462..7f265e5b 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -9,7 +9,6 @@ <string name="menu_help">Abi</string> <string name="menu_receive">Küsi raha</string> <string name="menu_rename">Nimeta ümber</string> - <string name="menu_archive">Arhiveeri</string> <string name="menu_backup">Tagavarakoopia</string> <string name="menu_changepw">Vaheta parooli</string> @@ -93,7 +92,6 @@ <string name="service_progress">Teen ettevalmistusi …\nSee võib aega võtta!</string> <string name="backup_failed">Tagavarakoopia ebaõnnestus!</string> - <string name="archive_failed">Arhiveerimine ebaõnnestus!</string> <string name="rename_failed">Ümber nimetamine ebaõnnestus!</string> <string name="changepw_failed">Parooli vahetamine ebaõnnestus!</string> <string name="changepw_success">Parool vahetatud</string> @@ -268,9 +266,8 @@ <string name="details_alert_no">Vii mind tagasi!</string> <string name="details_title">Lisainfo</string> - <string name="archive_alert_message">Rahakotist tehakse tagavarakoopia ja siis see kustutatakse!</string> - <string name="archive_alert_yes">Täpselt nii!</string> - <string name="archive_alert_no">Ei, tänan!</string> + <string name="delete_alert_yes">Täpselt nii!</string> + <string name="delete_alert_no">Ei, tänan!</string> <string name="fab_create_new">Loo uus rahakott</string> <string name="fab_restore_viewonly">Taasta rahakott vaatamiseks</string> @@ -420,4 +417,10 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="delete_alert_message">The wallet will be deleted!</string> + <string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> + <string name="delete_failed">Delete failed!</string> + + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 7e941929..98d688b1 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -9,7 +9,6 @@ <string name="menu_help">Aide</string> <string name="menu_receive">Recevoir</string> <string name="menu_rename">Renommer</string> - <string name="menu_archive">Archiver</string> <string name="menu_backup">Sauvegarder</string> <string name="menu_changepw">Modifier Phrase secrète</string> @@ -95,7 +94,6 @@ <string name="backup_success">Sauvegarde réussie</string> <string name="backup_failed">Sauvegarde Échouée !</string> - <string name="archive_failed">Archivage Échouée !</string> <string name="rename_failed">Renommage Échoué !</string> <string name="changepw_failed">Modification du mot de passe Échouée !</string> <string name="changepw_success">Modification du mot de passe réussie</string> @@ -269,9 +267,9 @@ <string name="details_alert_no">Non merci !</string> <string name="details_title">Détails</string> - <string name="archive_alert_message">Le portefeuille sera sauvegardé puis supprimé !</string> - <string name="archive_alert_yes">Oui, procéder !</string> - <string name="archive_alert_no">Non merci !</string> + <string name="delete_alert_message">Le portefeuille sera supprimé !</string> + <string name="delete_alert_yes">Oui, procéder !</string> + <string name="delete_alert_no">Non merci !</string> <string name="fab_create_new">Créer un nouveau portefeuille</string> <string name="fab_restore_viewonly">Restaurer un portefeuille d\'audit</string> @@ -426,4 +424,9 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> + <string name="delete_failed">Delete failed!</string> + + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index e9966d62..bcdbb142 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -9,7 +9,6 @@ <string name="menu_help">Segítség</string> <string name="menu_receive">Fogadás</string> <string name="menu_rename">Átnevezés</string> - <string name="menu_archive">Archiválás</string> <string name="menu_backup">Biztonsági mentés</string> <string name="menu_changepw">Jelszómódosítás</string> @@ -93,7 +92,6 @@ <string name="service_progress">Műveletek befejezése…\nEz eltarthat egy ideig!</string> <string name="backup_failed">Sikertelen biztonsági mentés!</string> - <string name="archive_failed">Sikertelen archiválás!</string> <string name="rename_failed">Sikertelen átnevezés!</string> <string name="changepw_failed">Sikertelen jelszómódosítás!</string> <string name="changepw_success">Jelszó megváltoztatva</string> @@ -266,9 +264,8 @@ <string name="details_alert_no">Inkább ne!</string> <string name="details_title">Részletek</string> - <string name="archive_alert_message">A tárcáról biztonsági másolat készül, majd törlésre fog kerülni!</string> - <string name="archive_alert_yes">Igen, mehet!</string> - <string name="archive_alert_no">Inkább ne!</string> + <string name="delete_alert_yes">Igen, mehet!</string> + <string name="delete_alert_no">Inkább ne!</string> <string name="fab_create_new">Új tárca létrehozása</string> <string name="fab_restore_viewonly">Figyelőtárca visszaállítása</string> @@ -424,4 +421,10 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="delete_alert_message">The wallet will be deleted!</string> + <string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> + <string name="delete_failed">Delete failed!</string> + + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 3ff7aa66..94b8b02f 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -9,7 +9,6 @@ <string name="menu_help">Aiuto</string> <string name="menu_receive">Ricevi</string> <string name="menu_rename">Rinomina</string> - <string name="menu_archive">Archivia</string> <string name="menu_backup">Backup</string> <string name="menu_changepw">Cambia passphrase</string> @@ -95,7 +94,6 @@ <string name="backup_success">Backup effettuato con successo</string> <string name="backup_failed">Backup fallito!</string> - <string name="archive_failed">Archiviazione fallita!</string> <string name="rename_failed">Rinomina fallita!</string> <string name="changepw_failed">Modifica password fallita!</string> <string name="changepw_success">Password cambiata</string> @@ -268,9 +266,9 @@ <string name="details_alert_no">Torna indietro!</string> <string name="details_title">Dettagli</string> - <string name="archive_alert_message">Il portafoglio verrà archiviato e poi eliminato!</string> - <string name="archive_alert_yes">Sì, procedi!</string> - <string name="archive_alert_no">No grazie!</string> + <string name="delete_alert_message">Il portafoglio verrà eliminato!</string> + <string name="delete_alert_yes">Sì, procedi!</string> + <string name="delete_alert_no">No grazie!</string> <string name="fab_create_new">Crea un nuovo portafoglio</string> <string name="fab_restore_viewonly">Recupera un portafoglio solo-visualizzazione</string> @@ -425,4 +423,9 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> + <string name="delete_failed">Delete failed!</string> + + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index ec6b209d..d5721daf 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -9,7 +9,6 @@ <string name="menu_help">ヘルプ</string> <string name="menu_receive">受取り</string> <string name="menu_rename">名前の変更</string> - <string name="menu_archive">アーカイブ</string> <string name="menu_backup">バックアップ</string> <string name="menu_changepw">パスフレーズの変更</string> @@ -93,7 +92,6 @@ <string name="service_progress">準備中 …\nしばらく時間がかかるかもしれません!</string> <string name="backup_failed">バックアップに失敗しました!</string> - <string name="archive_failed">アーカイブに失敗しました!</string> <string name="rename_failed">名前の変更に失敗しました!</string> <string name="changepw_failed">パスワード変更に失敗しました!</string> <string name="changepw_success">パスワードが変更されました</string> @@ -273,9 +271,8 @@ <string name="details_alert_no">戻ります!</string> <string name="details_title">詳細</string> - <string name="archive_alert_message">ウォレットはバックアップされてから削除されます!</string> - <string name="archive_alert_yes">はい、お願いします!</string> - <string name="archive_alert_no">いいえ、結構です!</string> + <string name="delete_alert_yes">はい、お願いします!</string> + <string name="delete_alert_no">いいえ、結構です!</string> <string name="fab_create_new">新しいウォレットの作成</string> <string name="fab_restore_viewonly">閲覧専用ウォレットを復元</string> @@ -425,4 +422,10 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="delete_alert_message">The wallet will be deleted!</string> + <string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> + <string name="delete_failed">Delete failed!</string> + + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 40bff1cd..1b2125fd 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -9,7 +9,6 @@ <string name="menu_help">Hjelp</string> <string name="menu_receive">Motta</string> <string name="menu_rename">Gi nytt navn</string> - <string name="menu_archive">Arkiver</string> <string name="menu_backup">Backup</string> <string name="menu_changepw">[Forandre passord]</string> @@ -93,7 +92,6 @@ <string name="backup_success">Backup suksessfull</string> <string name="backup_failed">Backup feila!</string> - <string name="archive_failed">Arkivering feila!</string> <string name="rename_failed">Kunne ikke gi nytt navn!</string> <string name="changepw_failed">[Passordforandring feila!]</string> <string name="changepw_success">[Passord forandra]</string> @@ -266,9 +264,8 @@ <string name="details_alert_no">Ta meg tilbake!</string> <string name="details_title">Detaljer</string> - <string name="archive_alert_message">Lommeboka vil bli backa opp og så sletta!</string> - <string name="archive_alert_yes">Ja, gjør det!</string> - <string name="archive_alert_no">Nei takk!</string> + <string name="delete_alert_yes">Ja, gjør det!</string> + <string name="delete_alert_no">Nei takk!</string> <string name="fab_create_new">Lag ny lommebok</string> <string name="fab_restore_viewonly">Gjenoprett bare-se lommebok</string> @@ -422,4 +419,10 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="delete_alert_message">The wallet will be deleted!</string> + <string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> + <string name="delete_failed">Delete failed!</string> + + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 119bfea0..f99fa2a6 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -9,7 +9,6 @@ <string name="menu_help">Help</string> <string name="menu_receive">Ontvangen</string> <string name="menu_rename">Hernoemen</string> - <string name="menu_archive">Archiveren</string> <string name="menu_backup">Back-up</string> <string name="menu_changepw">Wachtzin wijzigen</string> @@ -93,7 +92,6 @@ <string name="service_progress">Bezig met voltooien…\nDit kan even duren.</string> <string name="backup_failed">Back-up mislukt!</string> - <string name="archive_failed">Archiveren mislukt!</string> <string name="rename_failed">Hernoemen mislukt!</string> <string name="changepw_failed">Wachtwoord wijzigen mislukt!</string> <string name="changepw_success">Wachtwoord is gewijzigd</string> @@ -263,9 +261,9 @@ <string name="details_alert_no">Ga terug!</string> <string name="details_title">Details</string> - <string name="archive_alert_message">Er wordt een back-up gemaakt en daarna wordt de portemonnee verwijderd!</string> - <string name="archive_alert_yes">Ja, doe dat!</string> - <string name="archive_alert_no">Nee, niet doen!</string> + <string name="delete_alert_message">Er wordt de portemonnee verwijderd!</string> + <string name="delete_alert_yes">Ja, doe dat!</string> + <string name="delete_alert_no">Nee, niet doen!</string> <string name="fab_create_new">Nieuwe portemonnee</string> <string name="fab_restore_viewonly">Alleen-lezen portemonnee herstellen</string> @@ -422,4 +420,9 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> + <string name="delete_failed">Delete failed!</string> + + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 4fb7b1e0..d2af54fe 100755 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -9,7 +9,6 @@ <string name="menu_help">Ajuda</string> <string name="menu_receive">Receber</string> <string name="menu_rename">Renomear</string> - <string name="menu_archive">Arquivar</string> <string name="menu_backup">Backup</string> <string name="menu_changepw">Alterar Senha</string> @@ -93,7 +92,6 @@ <string name="service_progress">Finalizando o processo …\nPode demorar um pouco</string> <string name="backup_failed">Erro ao criar backup!</string> - <string name="archive_failed">Erro ao arquivar!</string> <string name="rename_failed">Erro ao renomear!</string> <string name="changepw_failed">Erro ao alterar senha!</string> <string name="changepw_success">Senha alterada</string> @@ -265,9 +263,8 @@ <string name="details_alert_no">Tire-me daqui!</string> <string name="details_title">Detalhes</string> - <string name="archive_alert_message">Farei um backup da carteira e depois vou deletá-la!</string> - <string name="archive_alert_yes">Sim, pode fazer!</string> - <string name="archive_alert_no">Não, obrigado!</string> + <string name="delete_alert_yes">Sim, pode fazer!</string> + <string name="delete_alert_no">Não, obrigado!</string> <string name="fab_create_new">Criar nova carteira</string> <string name="fab_restore_viewonly">Restaurar carteira \"Somente leitura\"</string> @@ -414,4 +411,10 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="delete_alert_message">The wallet will be deleted!</string> + <string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> + <string name="delete_failed">Delete failed!</string> + + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 7fc18b42..c9e70460 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -9,7 +9,6 @@ <string name="menu_help">Ajuda</string> <string name="menu_receive">Receber</string> <string name="menu_rename">Renomear</string> - <string name="menu_archive">Arquivar</string> <string name="menu_backup">Cópia de segurança</string> <string name="menu_changepw">Alterar palavra passe</string> @@ -93,7 +92,6 @@ <string name="service_progress">A concluir o processamento …\nIsto pode demorar!</string> <string name="backup_failed">Cópia de segurança falhou!</string> - <string name="archive_failed">Erro ao arquivar!</string> <string name="rename_failed">Erro ao renomear!</string> <string name="changepw_failed">Erro ao alterar a palavra passe!</string> <string name="changepw_success">Palavra passe alterada</string> @@ -265,9 +263,8 @@ <string name="details_alert_no">Volta atrás!</string> <string name="details_title">Detalhes</string> - <string name="archive_alert_message">Vai ser feita uma cópia de segurança da carteira e depois será apagada!</string> - <string name="archive_alert_yes">Sim, faz isso!</string> - <string name="archive_alert_no">Não obrigado!</string> + <string name="delete_alert_yes">Sim, faz isso!</string> + <string name="delete_alert_no">Não obrigado!</string> <string name="fab_create_new">Criar nova carteira</string> <string name="fab_restore_viewonly">Restaurar carteira apenas de visualização</string> @@ -426,4 +423,10 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="delete_alert_message">The wallet will be deleted!</string> + <string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> + <string name="delete_failed">Delete failed!</string> + + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 334aa8fe..54c92c24 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -9,7 +9,6 @@ <string name="menu_help">Ajutor</string> <string name="menu_receive">Primește</string> <string name="menu_rename">Redenumește</string> - <string name="menu_archive">Arhivă</string> <string name="menu_backup">Backup</string> <string name="password_weak">Continuă …</string> @@ -92,7 +91,6 @@ <string name="backup_success">Copia de rezervă creată cu succes</string> <string name="backup_failed">Copia de rezervă a eșuat!</string> - <string name="archive_failed">Arhivarea a eșuat!</string> <string name="rename_failed">Redenumirea a eșuat!</string> <string name="label_daemon">Nodul</string> @@ -241,9 +239,8 @@ <string name="details_alert_no">Du-mă înapoi!</string> <string name="details_title">Detalii</string> - <string name="archive_alert_message">Voi face o copie de siguranță a portofelului apoi va fi șters!</string> - <string name="archive_alert_yes">Da, fă asta!</string> - <string name="archive_alert_no">Nu mersi!</string> + <string name="delete_alert_yes">Da, fă asta!</string> + <string name="delete_alert_no">Nu mersi!</string> <string name="fab_create_new">Creeaza portofel nou</string> <string name="fab_restore_viewonly">Restaurează portofel-vizualizare</string> @@ -422,4 +419,10 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="delete_alert_message">The wallet will be deleted!</string> + <string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> + <string name="delete_failed">Delete failed!</string> + + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 9b68e19d..5d4bbff9 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -9,7 +9,6 @@ <string name="menu_help">Помощь</string> <string name="menu_receive">Получить</string> <string name="menu_rename">Переименовать</string> - <string name="menu_archive">Архив</string> <string name="menu_backup">Резервная копия</string> <string name="menu_changepw">Изменить пароль</string> @@ -94,7 +93,6 @@ <string name="service_progress">Для данного действия …\nМожет потребоваться некоторое время!</string> <string name="backup_failed">Резервное копирование произошло с ошибкой!</string> - <string name="archive_failed">Архивирование произошло с ошибкой!</string> <string name="rename_failed">Переименование произошло с ошибкой!</string> <string name="changepw_failed">Изменение пароля произошло с ошибкой!</string> <string name="changepw_success">Изменение пароля выполено успешно</string> @@ -267,9 +265,9 @@ <string name="details_alert_no">Верните меня обратно!</string> <string name="details_title">Подробная информация</string> - <string name="archive_alert_message">Этот кошелек будет скопирован, а затем удален!</string> - <string name="archive_alert_yes">Да, сделай это!</string> - <string name="archive_alert_no">Нет, спасибо!</string> + <string name="delete_alert_message">Этот кошелек будет удален!</string> + <string name="delete_alert_yes">Да, сделай это!</string> + <string name="delete_alert_no">Нет, спасибо!</string> <string name="fab_create_new">Создать новый кошелек</string> <string name="fab_restore_viewonly">Восстановить кошелек только для просмотра</string> @@ -426,4 +424,9 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> + <string name="delete_failed">Delete failed!</string> + + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index b34b33b6..c1895c2e 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -9,7 +9,6 @@ <string name="menu_help">Pomoc</string> <string name="menu_receive">Prijať</string> <string name="menu_rename">Premenovať</string> - <string name="menu_archive">Archivovať</string> <string name="menu_backup">Záloha</string> <string name="menu_changepw">Zmena Hesla</string> @@ -94,7 +93,6 @@ <string name="service_progress">Balím si veci …\nMôže to chvíľu trvať!</string> <string name="backup_failed">Zálohovanie zlyhalo!</string> - <string name="archive_failed">Archivácia zlyhala!</string> <string name="rename_failed">Premenovanie zlyhalo!</string> <string name="changepw_failed">Zmena hesla zlyhala!</string> <string name="changepw_success">Heslo zmenené</string> @@ -264,9 +262,9 @@ <string name="details_alert_no">Naspäť!</string> <string name="details_title">Detaily</string> - <string name="archive_alert_message">Peňaženka bude zálohovaná a následne zmazaná!</string> - <string name="archive_alert_yes">Áno, poďme na to!</string> - <string name="archive_alert_no">Nie, díky!</string> + <string name="delete_alert_message">Peňaženka bude zmazaná!</string> + <string name="delete_alert_yes">Áno, poďme na to!</string> + <string name="delete_alert_no">Nie, díky!</string> <string name="fab_create_new">Vytvor novú peňaženku</string> <string name="fab_restore_viewonly">Obnoviť prezeraciu peňaženku</string> @@ -423,4 +421,9 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> + <string name="delete_failed">Delete failed!</string> + + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 5abd7dae..7733d62a 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -9,7 +9,6 @@ <string name="menu_help">Pomoć</string> <string name="menu_receive">Primi</string> <string name="menu_rename">Preimenuj</string> - <string name="menu_archive">Arhiva</string> <string name="menu_backup">Bekap</string> <string name="menu_changepw">Promeni lozinku</string> @@ -94,7 +93,6 @@ <string name="backup_success">Bekap uspešan</string> <string name="backup_failed">Bekap neuspeo!</string> - <string name="archive_failed">Arhiviranje neuspelo!</string> <string name="rename_failed">Preimenovanje neuspelo!</string> <string name="changepw_failed">Promena lozinke neuspela!</string> <string name="changepw_success">Lozinka promenjena</string> @@ -274,9 +272,9 @@ <string name="details_alert_no">Vrati me nazad!</string> <string name="details_title">Detalji</string> - <string name="archive_alert_message">Novčanik će biti bekapovan a potom obrisan!</string> - <string name="archive_alert_yes">Da, uradi to!</string> - <string name="archive_alert_no">Ne, hvala!</string> + <string name="delete_alert_message">Novčanik će biti obrisan!</string> + <string name="delete_alert_yes">Da, uradi to!</string> + <string name="delete_alert_no">Ne, hvala!</string> <string name="fab_create_new">Kreiraj novi novčanik</string> <string name="fab_restore_viewonly">Obnovi novčanik za pregled</string> @@ -421,4 +419,9 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> + <string name="delete_failed">Delete failed!</string> + + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 7d760077..ea059569 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -9,7 +9,6 @@ <string name="menu_help">Hjälp</string> <string name="menu_receive">Ta emot</string> <string name="menu_rename">Byt namn</string> - <string name="menu_archive">Arkivera</string> <string name="menu_backup">Säkerhetskopiera</string> <string name="menu_changepw">Ändra lösenfras</string> @@ -94,7 +93,6 @@ <string name="backup_success">Säkerhetskopia sparad</string> <string name="backup_failed">Det gick inte att säkerhetskopiera!</string> - <string name="archive_failed">Det gick inte att arkivera!</string> <string name="rename_failed">Det gick inte att byta namn!</string> <string name="changepw_failed">Det gick inte att ändra lösenord!</string> <string name="changepw_success">Lösenordet har ändrats</string> @@ -254,9 +252,8 @@ <string name="details_alert_no">Ta mig tillbaka!</string> <string name="details_title">Detaljer</string> - <string name="archive_alert_message">Plånboken kommer att säkerhetskopieras och sedan tas bort!</string> - <string name="archive_alert_yes">Ja, gör det!</string> - <string name="archive_alert_no">Nej tack!</string> + <string name="delete_alert_yes">Ja, gör det!</string> + <string name="delete_alert_no">Nej tack!</string> <string name="fab_create_new">Skapa ny plånbok</string> <string name="fab_restore_viewonly">Återställ granskningsplånbok</string> @@ -414,4 +411,10 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="delete_alert_message">The wallet will be deleted!</string> + <string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> + <string name="delete_failed">Delete failed!</string> + + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 8f1de6b4..df166ffc 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -9,7 +9,6 @@ <string name="menu_help">Допомога</string> <string name="menu_receive">Отримати</string> <string name="menu_rename">Перейменувати</string> - <string name="menu_archive">Архів</string> <string name="menu_backup">Резервна копія</string> <string name="menu_changepw">Змінити пароль</string> @@ -94,7 +93,6 @@ <string name="service_progress">Для даної дії …\nМоже знадобитися деякий час!</string> <string name="backup_failed">Резервне копіювання відбулося з помилкою!</string> - <string name="archive_failed">Архівування відбулося з помилкою!</string> <string name="rename_failed">Перейменування відбулося з помилкою!</string> <string name="changepw_failed">Зміна паролю відбулася з помилкою!</string> <string name="changepw_success">Зміну паролю виконано успішно</string> @@ -267,9 +265,9 @@ <string name="details_alert_no">Поверніть мене назад!</string> <string name="details_title">Детальна інформація</string> - <string name="archive_alert_message">Цей гаманець буде скопійовано, а потім знищено!</string> - <string name="archive_alert_yes">Так, зроби це!</string> - <string name="archive_alert_no">Ні, дякую!</string> + <string name="delete_alert_message">Цей гаманець буде знищено!</string> + <string name="delete_alert_yes">Так, зроби це!</string> + <string name="delete_alert_no">Ні, дякую!</string> <string name="fab_create_new">Створити новий гаманець</string> <string name="fab_restore_viewonly">Відновити гаманець тільки для перегляду</string> @@ -426,4 +424,9 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> + <string name="delete_failed">Delete failed!</string> + + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 4bbd57a6..db38944e 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -7,7 +7,6 @@ <string name="menu_help">帮助</string> <string name="menu_receive">收款</string> <string name="menu_rename">重命名</string> - <string name="menu_archive">存档</string> <string name="menu_backup">备份</string> <string name="menu_changepw">修改密码</string> <string name="password_weak">强度不够哦 …</string> @@ -70,7 +69,6 @@ <string name="service_progress">整合中…\n这将花上不少时间</string> <string name="backup_success">备份成功</string> <string name="backup_failed">备份失败!</string> - <string name="archive_failed">存档失败!</string> <string name="rename_failed">重命名失败!</string> <string name="changepw_failed">密码修改失败!</string> <string name="changepw_success">密码已修改</string> @@ -213,9 +211,8 @@ <string name="details_alert_yes">安全</string> <string name="details_alert_no">返回上一步</string> <string name="details_title">详情</string> - <string name="archive_alert_message">即将开始备份钱包,完成后将删除钱包!</string> - <string name="archive_alert_yes">好的,开始吧!</string> - <string name="archive_alert_no">不用了,谢谢!</string> + <string name="delete_alert_yes">好的,开始吧!</string> + <string name="delete_alert_no">不用了,谢谢!</string> <string name="fab_create_new">创建新钱包</string> <string name="fab_restore_viewonly">恢复只读钱包</string> <string name="fab_restore_key">通过私钥恢复钱包</string> @@ -346,4 +343,10 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="delete_alert_message">The wallet will be deleted!</string> + <string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> + <string name="delete_failed">Delete failed!</string> + + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index d3b717cd..6f485452 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -9,7 +9,6 @@ <string name="menu_help">幫助</string> <string name="menu_receive">收款</string> <string name="menu_rename">重新命名</string> - <string name="menu_archive">封存</string> <string name="menu_backup">備份</string> <string name="menu_changepw">更改密碼</string> @@ -95,7 +94,6 @@ <string name="backup_success">備份成功</string> <string name="backup_failed">備份失敗!</string> - <string name="archive_failed">封存失敗!</string> <string name="rename_failed">重新命名失敗!</string> <string name="changepw_failed">密碼更改失敗!</string> <string name="changepw_success">密碼更改成功</string> @@ -265,9 +263,8 @@ <string name="details_alert_no">不看了!</string> <string name="details_title">詳細資訊</string> - <string name="archive_alert_message">錢包檔案將會被備份並且於列表中刪除!</string> - <string name="archive_alert_yes">好的!</string> - <string name="archive_alert_no">不了!</string> + <string name="delete_alert_yes">好的!</string> + <string name="delete_alert_no">不了!</string> <string name="fab_create_new">建立新錢包</string> <string name="fab_restore_viewonly">回復唯讀錢包</string> @@ -421,4 +418,10 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="delete_alert_message">The wallet will be deleted!</string> + <string name="menu_delete">Delete</string><!-- like: "Delete wallet!" --> + <string name="delete_failed">Delete failed!</string> + + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index acc9f129..7da9eea7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -10,7 +10,7 @@ <string name="menu_help">Help</string> <string name="menu_receive">Receive</string> <string name="menu_rename">Rename</string> - <string name="menu_archive">Archive</string> + <string name="menu_delete">Delete</string> <string name="menu_backup">Backup</string> <string name="menu_changepw">Change Passphrase</string> @@ -86,16 +86,16 @@ <string name="label_send_address">Destination Address</string> <string name="label_send_notes">Notes</string> - <string name="backup_progress">Backup in progress</string> - <string name="archive_progress">Archive in progress</string> - <string name="rename_progress">Rename in progress</string> + <string name="backup_progress">Backup in progress</string><!--TODO deleteme--> + <string name="archive_progress">Archive in progress</string><!--TODO deleteme--> + <string name="rename_progress">Rename in progress</string><!--TODO deleteme--> <string name="changepw_progress">Change Password in progress</string> <string name="service_progress">Wrapping things up …\nThis can take a while!</string> <string name="backup_success">Backup successful</string> <string name="backup_failed">Backup failed!</string> - <string name="archive_failed">Archive failed!</string> + <string name="delete_failed">Delete failed!</string> <string name="rename_failed">Rename failed!</string> <string name="changepw_failed">Change Password failed!</string> <string name="changepw_success">Password changed</string> @@ -132,7 +132,7 @@ <string name="bad_saved_password">Saved password is incorrect.\nPlease enter password manually.</string> <string name="bad_wallet">Wallet does not exist!</string> <string name="prompt_daemon_missing">Node must be set!</string> - <string name="prompt_wrong_net">Wallet does not match selected net</string> + <string name="prompt_wrong_net">Wallet does not match selected net</string><!--TODO deleteme--> <string name="label_watchonly">(Watch Only)</string> @@ -278,9 +278,9 @@ <string name="details_alert_no">Take me back!</string> <string name="details_title">Details</string> - <string name="archive_alert_message">The wallet will be backed up and then deleted!</string> - <string name="archive_alert_yes">Yes, do that!</string> - <string name="archive_alert_no">No thanks!</string> + <string name="delete_alert_message">The wallet will be deleted!</string> + <string name="delete_alert_yes">Yes, do that!</string> + <string name="delete_alert_no">No thanks!</string> <string-array name="currency" translatable="false"> <item>EUR</item> @@ -495,4 +495,6 @@ <string name="subaddress_select_label">Select a subaddress</string> <string name="subaddress_details_hint">Long-press for details</string> + <string name="menu_restore">Import wallet</string> + <string name="restore_failed">Import failed!</string> </resources>