From 34941c599a33329d4ce8f99b76b3eb638a2c7437 Mon Sep 17 00:00:00 2001 From: m2049r <30435443+m2049r@users.noreply.github.com> Date: Tue, 15 Aug 2017 23:59:41 +0200 Subject: [PATCH 01/10] Generate Wallet Fragment added --- app/src/main/cpp/monerujo.cpp | 2 +- .../m2049r/xmrwallet/GenerateFragment.java | 179 ++++++++++++++++++ .../com/m2049r/xmrwallet/LoginActivity.java | 140 +++++++++++++- .../com/m2049r/xmrwallet/LoginFragment.java | 41 ++-- .../com/m2049r/xmrwallet/WalletActivity.java | 4 +- .../com/m2049r/xmrwallet/WalletFragment.java | 14 +- .../m2049r/xmrwallet/model/WalletManager.java | 4 +- app/src/main/res/layout/gen_fragment.xml | 94 +++++++++ app/src/main/res/values/strings.xml | 10 + 9 files changed, 455 insertions(+), 33 deletions(-) create mode 100644 app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java create mode 100644 app/src/main/res/layout/gen_fragment.xml diff --git a/app/src/main/cpp/monerujo.cpp b/app/src/main/cpp/monerujo.cpp index 3c11364c..79be0389 100644 --- a/app/src/main/cpp/monerujo.cpp +++ b/app/src/main/cpp/monerujo.cpp @@ -463,7 +463,7 @@ Java_com_m2049r_xmrwallet_model_WalletManager_resolveOpenAlias(JNIEnv *env, jobj //TODO static std::tuple<bool, std::string, std::string, std::string, std::string> checkUpdates(const std::string &software, const std::string &subdir); -// actually a WalletManager function, but logically in Wallet +// actually a WalletManager function, but logically in onWalletSelected JNIEXPORT jboolean JNICALL Java_com_m2049r_xmrwallet_model_WalletManager_closeJ(JNIEnv *env, jobject instance, jobject walletInstance) { diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java new file mode 100644 index 00000000..8d1dfd85 --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java @@ -0,0 +1,179 @@ +package com.m2049r.xmrwallet; + +import android.app.Fragment; +import android.content.Context; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import java.io.File; + +public class GenerateFragment extends Fragment { + + EditText etWalletName; + EditText etWalletPassword; + Button bGenerate; + LinearLayout llAccept; + TextView tvWalletMnemonic; + Button bAccept; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View view = inflater.inflate(R.layout.gen_fragment, container, false); + + etWalletName = (EditText) view.findViewById(R.id.etWalletName); + etWalletPassword = (EditText) view.findViewById(R.id.etWalletPassword); + bGenerate = (Button) view.findViewById(R.id.bGenerate); + llAccept = (LinearLayout) view.findViewById(R.id.llAccept); + tvWalletMnemonic = (TextView) view.findViewById(R.id.tvWalletMnemonic); + bAccept = (Button) view.findViewById(R.id.bAccept); + + etWalletName.requestFocus(); + etWalletName.addTextChangedListener(new TextWatcher() { + @Override + public void afterTextChanged(Editable editable) { + setGenerateEnabled(); + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + }); + etWalletName.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(etWalletName, InputMethodManager.SHOW_IMPLICIT); + } + }); + etWalletName.setOnEditorActionListener(new TextView.OnEditorActionListener() { + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) { + etWalletPassword.requestFocus(); + return false; + } + return false; + } + }); + + etWalletPassword.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(etWalletPassword, InputMethodManager.SHOW_IMPLICIT); + } + }); + etWalletPassword.setOnEditorActionListener(new TextView.OnEditorActionListener() { + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) { + getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + generateWallet(); + return false; + } + return false; + } + }); + + bGenerate.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + generateWallet(); + } + }); + + bAccept.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + acceptWallet(); + } + }); + bAccept.setEnabled(false); + + return view; + } + + private void generateWallet() { + String name = etWalletName.getText().toString(); + if (name.length() == 0) return; + File walletFile = new File(activityCallback.getStorageRoot(), name + ".keys"); + if (walletFile.exists()) { + Toast.makeText(getActivity(), getString(R.string.generate_wallet_exists), Toast.LENGTH_LONG).show(); + return; + } + String password = etWalletPassword.getText().toString(); + bGenerate.setEnabled(false); + activityCallback.onGenerate(name, password); + } + + private void acceptWallet() { + String name = etWalletName.getText().toString(); + String password = etWalletPassword.getText().toString(); + bAccept.setEnabled(false); + activityCallback.onAccept(name, password); + } + + private void setGenerateEnabled() { + bGenerate.setEnabled(etWalletName.length() > 0); + } + + @Override + public void onResume() { + super.onResume(); + setGenerateEnabled(); + etWalletName.requestFocus(); + InputMethodManager imgr = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + imgr.showSoftInput(etWalletName, InputMethodManager.SHOW_IMPLICIT); + } + + public void showMnemonic(String mnemonic) { + setGenerateEnabled(); + if (mnemonic.length() > 0) { + tvWalletMnemonic.setText(mnemonic); + bAccept.setEnabled(true); + } else { + tvWalletMnemonic.setText(getActivity().getString(R.string.generate_seed)); + bAccept.setEnabled(false); + } + } + + GenerateFragment.Listener activityCallback; + + // Container Activity must implement this interface + public interface Listener { + void onGenerate(String name, String password); + + void onAccept(String name, String password); + + File getStorageRoot(); + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof GenerateFragment.Listener) { + this.activityCallback = (GenerateFragment.Listener) context; + } else { + throw new ClassCastException(context.toString() + + " must implement Listener"); + } + } + +} diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java index 9079c038..a5c3fe4c 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java @@ -19,6 +19,7 @@ package com.m2049r.xmrwallet; import android.app.Activity; import android.app.AlertDialog; import android.app.Fragment; +import android.app.FragmentTransaction; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -36,12 +37,15 @@ import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; +import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.model.WalletManager; +import com.m2049r.xmrwallet.service.MoneroHandlerThread; import com.m2049r.xmrwallet.util.Helper; import java.io.File; -public class LoginActivity extends Activity implements LoginFragment.LoginFragmentListener { +public class LoginActivity extends Activity + implements LoginFragment.Listener, GenerateFragment.Listener { static final String TAG = "LoginActivity"; static final int DAEMON_TIMEOUT = 500; // deamon must respond in 500ms @@ -62,7 +66,15 @@ public class LoginActivity extends Activity implements LoginFragment.LoginFragme // adapted from http://www.mkyong.com/android/android-prompt-user-input-dialog-example/ @Override - public void promptPassword(final String wallet) { + public void onWalletSelected(final String wallet) { + if (wallet.toLowerCase().startsWith("test")) { + startGenerateFragment(); + } else { + promptPassword(wallet); + } + } + + void promptPassword(final String wallet) { Context context = LoginActivity.this; LayoutInflater li = LayoutInflater.from(context); View promptsView = li.inflate(R.layout.prompt_password, null); @@ -130,7 +142,7 @@ public class LoginActivity extends Activity implements LoginFragment.LoginFragme } //////////////////////////////////////// - // LoginFragment.LoginFragmentListener + // LoginFragment.Listener //////////////////////////////////////// @Override public SharedPreferences getPrefs() { @@ -190,6 +202,126 @@ public class LoginActivity extends Activity implements LoginFragment.LoginFragme Fragment fragment = new LoginFragment(); getFragmentManager().beginTransaction() .add(R.id.fragment_container, fragment).commit(); - Log.d(TAG, "fragment added"); + Log.d(TAG, "LoginFragment added"); + } + + void startGenerateFragment() { + replaceFragment(new GenerateFragment()); + Log.d(TAG, "GenerateFragment placed"); + } + + void replaceFragment(Fragment newFragment) { + FragmentTransaction transaction = getFragmentManager().beginTransaction(); + transaction.replace(R.id.fragment_container, newFragment); + transaction.addToBackStack(null); + transaction.commit(); + } + + ////////////////////////////////////////// + // GenerateFragment.Listener + ////////////////////////////////////////// + static final String MNEMONIC_LANGUAGE = "English"; // see mnemonics/electrum-words.cpp for more + + @Override + public void onGenerate(final String name, final String password) { + final GenerateFragment genFragment = (GenerateFragment) + getFragmentManager().findFragmentById(R.id.fragment_container); + File newWalletFolder = new File(getStorageRoot(), ".new"); + if (!newWalletFolder.exists()) { + if (!newWalletFolder.mkdir()) { + Log.e(TAG, "Cannot create new wallet dir " + newWalletFolder.getAbsolutePath()); + genFragment.showMnemonic(""); + return; + } + } + if (!newWalletFolder.isDirectory()) { + Log.e(TAG, "New wallet dir " + newWalletFolder.getAbsolutePath() + "is not a directory"); + genFragment.showMnemonic(""); + return; + } + File cache = new File(newWalletFolder, name); + cache.delete(); + File keys = new File(newWalletFolder, name + ".keys"); + keys.delete(); + File address = new File(newWalletFolder, name + ".address.txt"); + address.delete(); + + if (cache.exists() || keys.exists() || address.exists()) { + Log.e(TAG, "Cannot remove all old wallet files: " + cache.getAbsolutePath()); + genFragment.showMnemonic(""); + return; + } + + final String newWalletPath = new File(newWalletFolder, name).getAbsolutePath(); + new Thread(null, + new Runnable() { + @Override + public void run() { + Log.d(TAG, "creating wallet " + newWalletPath); + Wallet newWallet = WalletManager.getInstance() + .createWallet(newWalletPath, password, MNEMONIC_LANGUAGE); + Log.d(TAG, "wallet created"); + Log.d(TAG, "Created " + newWallet.getAddress()); + Log.d(TAG, "Seed " + newWallet.getSeed() + "."); + final String mnemonic = newWallet.getSeed(); + newWallet.close(); + runOnUiThread(new Runnable() { + public void run() { + genFragment.showMnemonic(mnemonic); + } + }); + } + } + , "CreateWallet", MoneroHandlerThread.THREAD_STACK_SIZE).start(); + } + + @Override + public void onAccept(final String name, final String password) { + File newWalletFolder = new File(getStorageRoot(), ".new"); + if (!newWalletFolder.isDirectory()) { + Log.e(TAG, "New wallet dir " + newWalletFolder.getAbsolutePath() + "is not a directory"); + return; + } + final String newWalletPath = new File(newWalletFolder, name).getAbsolutePath(); + new Thread(null, + new Runnable() { + @Override + public void run() { + Log.d(TAG, "opening wallet " + newWalletPath); + Wallet newWallet = WalletManager.getInstance() + .openWallet(newWalletPath, password); + Wallet.Status status = newWallet.getStatus(); + Log.d(TAG, "wallet opened " + newWallet.getStatus()); + if (status != Wallet.Status.Status_Ok) { + Log.e(TAG, "New wallet is " + status.toString()); + runOnUiThread(new Runnable() { + public void run() { + Toast.makeText(LoginActivity.this, + getString(R.string.generate_wallet_create_failed), Toast.LENGTH_SHORT).show(); + } + }); + return; + } + final String walletPath = new File(getStorageRoot(), name).getAbsolutePath(); + final boolean rc = newWallet.store(walletPath); + Log.d(TAG, "wallet stored with rc=" + rc); + newWallet.close(); + Log.d(TAG, "wallet closed"); + runOnUiThread(new Runnable() { + public void run() { + if (rc) { + getFragmentManager().popBackStack(); + Toast.makeText(LoginActivity.this, + getString(R.string.generate_wallet_created), Toast.LENGTH_SHORT).show(); + } else { + Log.e(TAG, "Wallet store failed to " + walletPath); + Toast.makeText(LoginActivity.this, + getString(R.string.generate_wallet_create_failed), Toast.LENGTH_SHORT).show(); + } + } + }); + } + } + , "AcceptWallet", MoneroHandlerThread.THREAD_STACK_SIZE).start(); } } diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java index f925d08a..0b3e515f 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java @@ -45,38 +45,49 @@ import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.util.ArrayList; +import java.util.Comparator; import java.util.Date; import java.util.List; +import java.util.Set; +import java.util.TreeSet; public class LoginFragment extends Fragment { private static final String TAG = "LoginFragment"; + public static final int WALLETNAME_PREAMBLE_LENGTH = "[123456] ".length(); + ListView listView; - List<String> walletList = new ArrayList<>(); + Set<String> walletList = new TreeSet<>(new Comparator<String>() { + @Override + public int compare(String o1, String o2) { + return o1.substring(WALLETNAME_PREAMBLE_LENGTH).toLowerCase() + .compareTo(o2.substring(WALLETNAME_PREAMBLE_LENGTH).toLowerCase()); + } + }); List<String> displayedList = new ArrayList<>(); ToggleButton tbMainNet; EditText etDaemonAddress; - LoginFragment.LoginFragmentListener activityCallback; + Listener activityCallback; // Container Activity must implement this interface - public interface LoginFragmentListener { + public interface Listener { SharedPreferences getPrefs(); File getStorageRoot(); - void promptPassword(final String wallet); + void onWalletSelected(final String wallet); } @Override public void onAttach(Context context) { super.onAttach(context); - if (context instanceof LoginFragment.LoginFragmentListener) { - this.activityCallback = (LoginFragment.LoginFragmentListener) context; + if (context instanceof Listener) { + this.activityCallback = (Listener) context; } else { throw new ClassCastException(context.toString() - + " must implement WalletFragmentListener"); + + " must implement Listener"); } } @@ -148,14 +159,13 @@ public class LoginFragment extends Fragment { } String itemValue = (String) listView.getItemAtPosition(position); - if ((isMainNet() && itemValue.charAt(1) != '4') - || (!isMainNet() && itemValue.charAt(1) != '9')) { + String x = isMainNet() ? "4" : "9A"; + if (x.indexOf(itemValue.charAt(1)) < 0) { Toast.makeText(getActivity(), getString(R.string.prompt_wrong_net), Toast.LENGTH_LONG).show(); return; } - final int preambleLength = "[123456] ".length(); - if (itemValue.length() <= (preambleLength)) { + if (itemValue.length() <= (WALLETNAME_PREAMBLE_LENGTH)) { Toast.makeText(getActivity(), getString(R.string.panic), Toast.LENGTH_LONG).show(); return; } @@ -167,8 +177,8 @@ public class LoginFragment extends Fragment { // looking good savePrefs(false); - String wallet = itemValue.substring(preambleLength); - activityCallback.promptPassword(wallet); + String wallet = itemValue.substring(WALLETNAME_PREAMBLE_LENGTH); + activityCallback.onWalletSelected(wallet); } }); loadList(); @@ -177,9 +187,10 @@ public class LoginFragment extends Fragment { private void filterList() { displayedList.clear(); - char x = isMainNet() ? '4' : '9'; + String x = isMainNet() ? "4" : "9A"; for (String s : walletList) { - if (s.charAt(1) == x) displayedList.add(s); + Log.d(TAG, "filtering " + s); + if (x.indexOf(s.charAt(1)) >= 0) displayedList.add(s); } } diff --git a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java index 40ef7122..8e101015 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java @@ -31,7 +31,7 @@ import android.widget.Toast; import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.service.WalletService; -public class WalletActivity extends Activity implements WalletFragment.WalletFragmentListener, +public class WalletActivity extends Activity implements WalletFragment.Listener, WalletService.Observer { private static final String TAG = "WalletActivity"; @@ -212,7 +212,7 @@ public class WalletActivity extends Activity implements WalletFragment.WalletFra } ////////////////////////////////////////// - // WalletFragment.WalletFragmentListener + // WalletFragment.Listener ////////////////////////////////////////// @Override diff --git a/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java b/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java index 50791bdd..ee4a636d 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java @@ -188,10 +188,10 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O connectionStatusView.setText(net + " " + daemonConnected.toString().substring(17)); } - WalletFragmentListener activityCallback; + Listener activityCallback; // Container Activity must implement this interface - public interface WalletFragmentListener { + public interface Listener { boolean hasBoundService(); Wallet.ConnectionStatus getConnectionStatus(); @@ -205,15 +205,11 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O @Override public void onAttach(Context context) { super.onAttach(context); - if (context instanceof WalletFragmentListener) { - this.activityCallback = (WalletFragmentListener) context; + if (context instanceof Listener) { + this.activityCallback = (Listener) context; } else { throw new ClassCastException(context.toString() - + " must implement WalletFragmentListener"); + + " must implement Listener"); } } - - private void runOnUiThread(Runnable runnable) { - if (isAdded()) getActivity().runOnUiThread(runnable); - } } 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 2757877f..54782cf2 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/model/WalletManager.java +++ b/app/src/main/java/com/m2049r/xmrwallet/model/WalletManager.java @@ -57,7 +57,7 @@ public class WalletManager { private void manageWallet(String walletId, Wallet wallet) { if (getWallet(walletId) != null) { - throw new IllegalStateException("Wallet already under management!"); + throw new IllegalStateException(walletId + " already under management!"); } Log.d(TAG, "Managing " + walletId); managedWallets.put(walletId, wallet); @@ -65,7 +65,7 @@ public class WalletManager { private void unmanageWallet(String walletId) { if (getWallet(walletId) == null) { - throw new IllegalStateException("Wallet not under management!"); + throw new IllegalStateException(walletId + " not under management!"); } Log.d(TAG, "Unmanaging " + walletId); managedWallets.remove(walletId); diff --git a/app/src/main/res/layout/gen_fragment.xml b/app/src/main/res/layout/gen_fragment.xml new file mode 100644 index 00000000..6a6edbc1 --- /dev/null +++ b/app/src/main/res/layout/gen_fragment.xml @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="16dp" + android:gravity="center" + android:text="@string/generate_title" + android:textSize="30sp" /> + + <EditText + android:id="@+id/etWalletName" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="16dp" + android:layout_marginEnd="8dp" + android:layout_marginStart="8dp" + android:layout_marginTop="8dp" + android:backgroundTint="@color/colorPrimary" + android:focusable="true" + android:gravity="center" + android:hint="@string/generate_name" + android:imeOptions="actionDone" + android:inputType="text" + android:maxLines="1" + android:textIsSelectable="true" + android:textSize="24sp" /> + + <EditText + android:id="@+id/etWalletPassword" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="16dp" + android:layout_marginEnd="8dp" + android:layout_marginStart="8dp" + android:layout_marginTop="8dp" + android:backgroundTint="@color/colorPrimary" + android:focusable="true" + android:gravity="center" + android:hint="@string/generate_password" + android:imeOptions="actionDone" + android:inputType="text" + android:maxLines="1" + android:textIsSelectable="true" + android:textSize="24sp" /> + + <Button + android:id="@+id/bGenerate" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="16dp" + android:layout_marginLeft="16dp" + android:layout_marginRight="16dp" + android:layout_marginTop="8dp" + android:background="@color/colorPrimary" + android:text="@string/generate_buttonGenerate" /> + + <LinearLayout + android:id="@+id/llAccept" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center" + android:orientation="vertical" + android:visibility="visible"> + + <TextView + android:id="@+id/tvWalletMnemonic" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="8dp" + android:layout_marginEnd="8dp" + android:layout_marginStart="8dp" + android:layout_marginTop="8dp" + android:text="@string/generate_seed" + android:textAlignment="center" + android:textColor="@color/menu_background_color" + android:textSize="24sp" /> + + <Button + android:id="@+id/bAccept" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="16dp" + android:layout_marginLeft="16dp" + android:layout_marginRight="16dp" + android:layout_marginTop="8dp" + android:background="@color/colorPrimary" + android:text="@string/generate_buttonAccept" /> + </LinearLayout> +</LinearLayout> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f2c64603..ed9e64f8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -43,4 +43,14 @@ <string name="message_strorage_not_writable">External Storage is not writable! Panic!</string> <string name="message_strorage_not_permitted">We really need those External Storage permissions!</string> + <string name="generate_title">Generate Wallet</string> + <string name="generate_name">Wallet Name</string> + <string name="generate_password">Wallet Password</string> + <string name="generate_buttonGenerate">Do it already!</string> + <string name="generate_seed">Mnemonic Seed</string> + <string name="generate_buttonAccept">I have noted this mnemonic seed\nNow, I want to loose all my money!</string> + <string name="generate_wallet_exists">Wallet exists!\nChoose another name</string> + <string name="generate_wallet_created">Wallet created</string> + <string name="generate_wallet_create_failed">Wallet create failed</string> + </resources> From 142885821e4161010bd11bae7a33d4273f88af88 Mon Sep 17 00:00:00 2001 From: m2049r <30435443+m2049r@users.noreply.github.com> Date: Wed, 16 Aug 2017 00:28:53 +0200 Subject: [PATCH 02/10] Generate Wallet command added to wallet list --- .../com/m2049r/xmrwallet/GenerateFragment.java | 3 +++ .../java/com/m2049r/xmrwallet/LoginActivity.java | 3 ++- .../java/com/m2049r/xmrwallet/LoginFragment.java | 16 ++++++++++------ app/src/main/res/values/strings.xml | 2 +- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java index 8d1dfd85..abaef246 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java @@ -95,6 +95,7 @@ public class GenerateFragment extends Fragment { bGenerate.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + // TODO make keyboard go away generateWallet(); } }); @@ -105,6 +106,7 @@ public class GenerateFragment extends Fragment { acceptWallet(); } }); + bAccept.setEnabled(false); return view; @@ -116,6 +118,7 @@ public class GenerateFragment extends Fragment { File walletFile = new File(activityCallback.getStorageRoot(), name + ".keys"); if (walletFile.exists()) { Toast.makeText(getActivity(), getString(R.string.generate_wallet_exists), Toast.LENGTH_LONG).show(); + etWalletName.requestFocus(); return; } String password = etWalletPassword.getText().toString(); diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java index a5c3fe4c..f3a2b6af 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java @@ -67,7 +67,8 @@ public class LoginActivity extends Activity // adapted from http://www.mkyong.com/android/android-prompt-user-input-dialog-example/ @Override public void onWalletSelected(final String wallet) { - if (wallet.toLowerCase().startsWith("test")) { + Log.d(TAG, "selected wallet is ." + wallet + "."); + if (wallet.equals(getString(R.string.generate_title))) { startGenerateFragment(); } else { promptPassword(wallet); diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java index 0b3e515f..f79603d4 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java @@ -53,7 +53,8 @@ import java.util.TreeSet; public class LoginFragment extends Fragment { private static final String TAG = "LoginFragment"; - public static final int WALLETNAME_PREAMBLE_LENGTH = "[123456] ".length(); + public static final String WALLETNAME_PREAMBLE = "[------] "; + public static final int WALLETNAME_PREAMBLE_LENGTH = WALLETNAME_PREAMBLE.length(); ListView listView; @@ -159,16 +160,18 @@ public class LoginFragment extends Fragment { } String itemValue = (String) listView.getItemAtPosition(position); - String x = isMainNet() ? "4" : "9A"; - if (x.indexOf(itemValue.charAt(1)) < 0) { - Toast.makeText(getActivity(), getString(R.string.prompt_wrong_net), Toast.LENGTH_LONG).show(); - return; - } if (itemValue.length() <= (WALLETNAME_PREAMBLE_LENGTH)) { Toast.makeText(getActivity(), getString(R.string.panic), Toast.LENGTH_LONG).show(); return; } + + String x = isMainNet() ? "4-" : "9A-"; + if (x.indexOf(itemValue.charAt(1)) < 0) { + Toast.makeText(getActivity(), getString(R.string.prompt_wrong_net), Toast.LENGTH_LONG).show(); + return; + } + if (!checkAndSetWalletDaemon(getDaemon(), !isMainNet())) { Toast.makeText(getActivity(), getString(R.string.warn_daemon_unavailable), Toast.LENGTH_LONG).show(); return; @@ -192,6 +195,7 @@ public class LoginFragment extends Fragment { Log.d(TAG, "filtering " + s); if (x.indexOf(s.charAt(1)) >= 0) displayedList.add(s); } + displayedList.add(WALLETNAME_PREAMBLE + getString(R.string.generate_title)); } private void loadList() { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ed9e64f8..bb74bf3b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -18,7 +18,7 @@ <string name="bad_password">Bad password!</string> <string name="prompt_daemon_missing">Daemon address must be set!</string> <string name="prompt_wrong_net">Daemon type does not fit to wallet!</string> - <string name="warn_daemon_unavailable">Cannot connect to daemon!</string> + <string name="warn_daemon_unavailable">Cannot connect to daemon! Try again.</string> <string name="panic">Something\'s wrong!</string> <string name="title_amount">Amount</string> From 3239bdeb338440cf8b663e5f404711f1bbdd6bbf Mon Sep 17 00:00:00 2001 From: m2049r <30435443+m2049r@users.noreply.github.com> Date: Wed, 16 Aug 2017 00:49:48 +0200 Subject: [PATCH 03/10] removed default localhost daemons --- app/app.iml | 1 + app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/app.iml b/app/app.iml index a6fa2860..25bc19b1 100644 --- a/app/app.iml +++ b/app/app.iml @@ -101,6 +101,7 @@ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-support" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/reload-dex" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/restart-dex" /> diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java index f79603d4..e7f1bf96 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java @@ -245,8 +245,8 @@ public class LoginFragment extends Fragment { SharedPreferences sharedPref = activityCallback.getPrefs(); boolean mainnet = sharedPref.getBoolean(PREF_MAINNET, false); - daemonMainNet = sharedPref.getString(PREF_DAEMON_MAINNET, "localhost:18081"); - daemonTestNet = sharedPref.getString(PREF_DAEMON_TESTNET, "localhost:28081"); + daemonMainNet = sharedPref.getString(PREF_DAEMON_MAINNET, ""); + daemonTestNet = sharedPref.getString(PREF_DAEMON_TESTNET, ""); setMainNet(mainnet); if (mainnet) { From 56132e26eda980e309868af38b390bbf562a17c6 Mon Sep 17 00:00:00 2001 From: m2049r <30435443+m2049r@users.noreply.github.com> Date: Wed, 16 Aug 2017 10:41:56 +0200 Subject: [PATCH 04/10] tweaks - mostly keyboard & UI --- .../m2049r/xmrwallet/GenerateFragment.java | 67 ++++++++++++------- .../com/m2049r/xmrwallet/LoginActivity.java | 46 ++++++++----- .../com/m2049r/xmrwallet/LoginFragment.java | 17 ++--- .../com/m2049r/xmrwallet/WalletActivity.java | 14 ++-- .../xmrwallet/service/WalletService.java | 3 + .../com/m2049r/xmrwallet/util/Helper.java | 25 +++++++ app/src/main/res/layout/gen_fragment.xml | 21 +++++- app/src/main/res/values/strings.xml | 11 ++- 8 files changed, 146 insertions(+), 58 deletions(-) diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java index abaef246..9fc842d3 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java @@ -9,26 +9,31 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.WindowManager; import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; +import com.m2049r.xmrwallet.model.WalletManager; +import com.m2049r.xmrwallet.util.Helper; + import java.io.File; +// TODO: somehow show which net we are generating for + public class GenerateFragment extends Fragment { EditText etWalletName; EditText etWalletPassword; Button bGenerate; LinearLayout llAccept; + TextView tvWalletAddress; TextView tvWalletMnemonic; Button bAccept; + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -39,14 +44,20 @@ public class GenerateFragment extends Fragment { etWalletPassword = (EditText) view.findViewById(R.id.etWalletPassword); bGenerate = (Button) view.findViewById(R.id.bGenerate); llAccept = (LinearLayout) view.findViewById(R.id.llAccept); + tvWalletAddress = (TextView) view.findViewById(R.id.tvWalletAddress); tvWalletMnemonic = (TextView) view.findViewById(R.id.tvWalletMnemonic); bAccept = (Button) view.findViewById(R.id.bAccept); + boolean testnet = WalletManager.getInstance().isTestNet(); + tvWalletMnemonic.setTextIsSelectable(testnet); + etWalletName.requestFocus(); + Helper.showKeyboard(getActivity()); + setGenerateEnabled(); etWalletName.addTextChangedListener(new TextWatcher() { @Override public void afterTextChanged(Editable editable) { - setGenerateEnabled(); + showMnemonic(""); } @Override @@ -60,14 +71,15 @@ public class GenerateFragment extends Fragment { etWalletName.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(etWalletName, InputMethodManager.SHOW_IMPLICIT); + Helper.showKeyboard(getActivity()); } }); etWalletName.setOnEditorActionListener(new TextView.OnEditorActionListener() { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) { - etWalletPassword.requestFocus(); + if (etWalletName.length() > 0) { + etWalletPassword.requestFocus(); + } // otherwise ignore return false; } return false; @@ -77,14 +89,27 @@ public class GenerateFragment extends Fragment { etWalletPassword.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(etWalletPassword, InputMethodManager.SHOW_IMPLICIT); + Helper.showKeyboard(getActivity()); + } + }); + etWalletPassword.addTextChangedListener(new TextWatcher() { + @Override + public void afterTextChanged(Editable editable) { + showMnemonic(""); + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { } }); etWalletPassword.setOnEditorActionListener(new TextView.OnEditorActionListener() { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) { - getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + Helper.hideKeyboard(getActivity()); generateWallet(); return false; } @@ -95,7 +120,7 @@ public class GenerateFragment extends Fragment { bGenerate.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - // TODO make keyboard go away + Helper.hideKeyboard(getActivity()); generateWallet(); } }); @@ -108,6 +133,7 @@ public class GenerateFragment extends Fragment { }); bAccept.setEnabled(false); + llAccept.setVisibility(View.GONE); return view; } @@ -115,8 +141,8 @@ public class GenerateFragment extends Fragment { private void generateWallet() { String name = etWalletName.getText().toString(); if (name.length() == 0) return; - File walletFile = new File(activityCallback.getStorageRoot(), name + ".keys"); - if (walletFile.exists()) { + String walletPath = Helper.getWalletPath(getActivity(), name); + if (WalletManager.getInstance().walletExists(walletPath)) { Toast.makeText(getActivity(), getString(R.string.generate_wallet_exists), Toast.LENGTH_LONG).show(); etWalletName.requestFocus(); return; @@ -137,23 +163,18 @@ public class GenerateFragment extends Fragment { bGenerate.setEnabled(etWalletName.length() > 0); } - @Override - public void onResume() { - super.onResume(); - setGenerateEnabled(); - etWalletName.requestFocus(); - InputMethodManager imgr = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); - imgr.showSoftInput(etWalletName, InputMethodManager.SHOW_IMPLICIT); - } - public void showMnemonic(String mnemonic) { setGenerateEnabled(); if (mnemonic.length() > 0) { tvWalletMnemonic.setText(mnemonic); bAccept.setEnabled(true); + llAccept.setVisibility(View.VISIBLE); } else { - tvWalletMnemonic.setText(getActivity().getString(R.string.generate_seed)); - bAccept.setEnabled(false); + if (llAccept.getVisibility() != View.GONE) { + tvWalletMnemonic.setText(getActivity().getString(R.string.generate_seed)); + bAccept.setEnabled(false); + llAccept.setVisibility(View.GONE); + } } } diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java index f3a2b6af..b9d2c757 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java @@ -33,6 +33,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; @@ -64,18 +65,26 @@ public class LoginActivity extends Activity } } - // adapted from http://www.mkyong.com/android/android-prompt-user-input-dialog-example/ @Override - public void onWalletSelected(final String wallet) { - Log.d(TAG, "selected wallet is ." + wallet + "."); - if (wallet.equals(getString(R.string.generate_title))) { + public void onWalletSelected(final String walletName) { + Log.d(TAG, "selected wallet is ." + walletName + "."); + if (walletName.equals(':' + getString(R.string.generate_title))) { startGenerateFragment(); } else { - promptPassword(wallet); + // now it's getting real, check if wallet exists + String walletPath = Helper.getWalletPath(this, walletName); + if (WalletManager.getInstance().walletExists(walletPath)) { + promptPassword(walletName); + } else { // this cannot really happen as we prefilter choices + Toast.makeText(this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show(); + } } } + AlertDialog passwordDialog = null; // for preventing multiple clicks in wallet list + void promptPassword(final String wallet) { + if (passwordDialog != null) return; // we are already asking for password Context context = LoginActivity.this; LayoutInflater li = LayoutInflater.from(context); View promptsView = li.inflate(R.layout.prompt_password, null); @@ -94,37 +103,40 @@ public class LoginActivity extends Activity .setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { - getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + Helper.hideKeyboardAlways(LoginActivity.this); String pass = etPassword.getText().toString(); processPasswordEntry(wallet, pass); + passwordDialog = null; } }) .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { - getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + Helper.hideKeyboardAlways(LoginActivity.this); dialog.cancel(); + passwordDialog = null; } }); - final AlertDialog alertDialog = alertDialogBuilder.create(); - // request keyboard - alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); + passwordDialog = alertDialogBuilder.create(); + Helper.showKeyboard(passwordDialog); + // accept keyboard "ok" etPassword.setOnEditorActionListener(new TextView.OnEditorActionListener() { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) { - getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + Helper.hideKeyboardAlways(LoginActivity.this); String pass = etPassword.getText().toString(); - alertDialog.cancel(); + passwordDialog.cancel(); processPasswordEntry(wallet, pass); + passwordDialog = null; return false; } return false; } }); - alertDialog.show(); + passwordDialog.show(); } private boolean checkWalletPassword(String walletName, String password) { @@ -191,7 +203,7 @@ public class LoginActivity extends Activity } else { String msg = getString(R.string.message_strorage_not_permitted); Log.e(TAG, msg); - Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); + Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); //throw new IllegalStateException(msg); } break; @@ -298,9 +310,10 @@ public class LoginActivity extends Activity runOnUiThread(new Runnable() { public void run() { Toast.makeText(LoginActivity.this, - getString(R.string.generate_wallet_create_failed), Toast.LENGTH_SHORT).show(); + getString(R.string.generate_wallet_create_failed_1), Toast.LENGTH_LONG).show(); } }); + newWallet.close(); return; } final String walletPath = new File(getStorageRoot(), name).getAbsolutePath(); @@ -317,7 +330,7 @@ public class LoginActivity extends Activity } else { Log.e(TAG, "Wallet store failed to " + walletPath); Toast.makeText(LoginActivity.this, - getString(R.string.generate_wallet_create_failed), Toast.LENGTH_SHORT).show(); + getString(R.string.generate_wallet_create_failed_2), Toast.LENGTH_LONG).show(); } } }); @@ -325,4 +338,5 @@ public class LoginActivity extends Activity } , "AcceptWallet", MoneroHandlerThread.THREAD_STACK_SIZE).start(); } + } diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java index e7f1bf96..3ab58db9 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java @@ -26,9 +26,7 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.WindowManager; import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.BaseAdapter; @@ -39,6 +37,7 @@ import android.widget.Toast; import android.widget.ToggleButton; import com.m2049r.xmrwallet.model.WalletManager; +import com.m2049r.xmrwallet.util.Helper; import java.io.File; import java.io.IOException; @@ -108,19 +107,18 @@ public class LoginFragment extends Fragment { tbMainNet = (ToggleButton) view.findViewById(R.id.tbMainNet); etDaemonAddress = (EditText) view.findViewById(R.id.etDaemonAddress); - getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); + Helper.hideKeyboard(getActivity()); etDaemonAddress.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(etDaemonAddress, InputMethodManager.SHOW_IMPLICIT); + Helper.showKeyboard(getActivity()); } }); etDaemonAddress.setOnEditorActionListener(new TextView.OnEditorActionListener() { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) { - getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + Helper.hideKeyboard(getActivity()); return false; } return false; @@ -155,7 +153,9 @@ public class LoginFragment extends Fragment { public void onItemClick(AdapterView<?> parent, View view, int position, long id) { EditText tvDaemonAddress = (EditText) getView().findViewById(R.id.etDaemonAddress); if (tvDaemonAddress.getText().toString().length() == 0) { - Toast.makeText(getActivity(), getString(R.string.prompt_daemon_missing), Toast.LENGTH_LONG).show(); + Toast.makeText(getActivity(), getString(R.string.prompt_daemon_missing), Toast.LENGTH_SHORT).show(); + tvDaemonAddress.requestFocus(); + Helper.showKeyboard(getActivity()); return; } @@ -173,7 +173,7 @@ public class LoginFragment extends Fragment { } if (!checkAndSetWalletDaemon(getDaemon(), !isMainNet())) { - Toast.makeText(getActivity(), getString(R.string.warn_daemon_unavailable), Toast.LENGTH_LONG).show(); + Toast.makeText(getActivity(), getString(R.string.warn_daemon_unavailable), Toast.LENGTH_SHORT).show(); return; } @@ -181,6 +181,7 @@ public class LoginFragment extends Fragment { savePrefs(false); String wallet = itemValue.substring(WALLETNAME_PREAMBLE_LENGTH); + if (itemValue.charAt(1) == '-') wallet = ':' + wallet; activityCallback.onWalletSelected(wallet); } }); diff --git a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java index 8e101015..c1c2bcb8 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java @@ -200,11 +200,6 @@ public class WalletActivity extends Activity implements WalletFragment.Listener, Intent intent = new Intent(getApplicationContext(), WalletService.class); intent.putExtra(WalletService.REQUEST, WalletService.REQUEST_CMD_STORE); startService(intent); - runOnUiThread(new Runnable() { - public void run() { - Toast.makeText(getApplicationContext(), getString(R.string.status_wallet_unloading), Toast.LENGTH_LONG).show(); - } - }); Log.d(TAG, "STORE request sent"); } else { Log.e(TAG, "Service not bound"); @@ -259,6 +254,15 @@ public class WalletActivity extends Activity implements WalletFragment.Listener, }); } + @Override + public void onWalletStored() { + runOnUiThread(new Runnable() { + public void run() { + Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_unloaded), Toast.LENGTH_SHORT).show(); + } + }); + } + @Override public void onProgress(final String text) { //Log.d(TAG, "PROGRESS: " + text); diff --git a/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java b/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java index 436d08f0..b40f3a8e 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java +++ b/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java @@ -191,6 +191,8 @@ public class WalletService extends Service { void onProgress(String text); void onProgress(int n); + + void onWalletStored(); } String progressText = null; @@ -257,6 +259,7 @@ public class WalletService extends Service { Log.d(TAG, "storing wallet: " + myWallet.getName()); getWallet().store(); Log.d(TAG, "wallet stored: " + myWallet.getName()); + if (observer != null) observer.onWalletStored(); } } break; 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 873c89f1..182daf57 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java +++ b/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java @@ -18,10 +18,13 @@ package com.m2049r.xmrwallet.util; import android.Manifest; import android.app.Activity; +import android.app.Dialog; import android.content.Context; import android.content.pm.PackageManager; import android.os.Environment; import android.util.Log; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; import com.m2049r.xmrwallet.R; @@ -82,4 +85,26 @@ public class Helper { return Environment.MEDIA_MOUNTED.equals(state); } + static public void showKeyboard(Activity act) { + InputMethodManager imm = (InputMethodManager) act.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(act.getCurrentFocus(), InputMethodManager.SHOW_IMPLICIT); + } + + static public void hideKeyboard(Activity act) { + if (act.getCurrentFocus() == null) { + act.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); + } else { + InputMethodManager imm = (InputMethodManager) act.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow((null == act.getCurrentFocus()) ? null : act.getCurrentFocus().getWindowToken(), + InputMethodManager.HIDE_NOT_ALWAYS); + } + } + + static public void showKeyboard(Dialog dialog) { + dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); + } + + static public void hideKeyboardAlways(Activity act) { + act.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + } } diff --git a/app/src/main/res/layout/gen_fragment.xml b/app/src/main/res/layout/gen_fragment.xml index 6a6edbc1..e0ed6a90 100644 --- a/app/src/main/res/layout/gen_fragment.xml +++ b/app/src/main/res/layout/gen_fragment.xml @@ -52,7 +52,7 @@ android:id="@+id/bGenerate" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="16dp" + android:layout_marginBottom="8dp" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" android:layout_marginTop="8dp" @@ -67,6 +67,21 @@ android:orientation="vertical" android:visibility="visible"> + <TextView + android:id="@+id/tvWalletAddress" + android:textIsSelectable="true" + android:selectAllOnFocus="true" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="8dp" + android:layout_marginEnd="8dp" + android:layout_marginStart="8dp" + android:layout_marginTop="8dp" + android:text="@string/generate_address_placeholder" + android:textAlignment="center" + android:textColor="@color/menu_background_color" + android:textSize="12sp" /> + <TextView android:id="@+id/tvWalletMnemonic" android:layout_width="match_parent" @@ -75,10 +90,10 @@ android:layout_marginEnd="8dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" - android:text="@string/generate_seed" + android:text="@string/generate_mnemonic_placeholder" android:textAlignment="center" android:textColor="@color/menu_background_color" - android:textSize="24sp" /> + android:textSize="20sp" /> <Button android:id="@+id/bAccept" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bb74bf3b..09cfd3be 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -11,13 +11,15 @@ <string name="status_walletlist_loading">Loading Wallet List</string> <string name="status_wallet_loading">Loading Wallet …</string> <string name="status_wallet_unloading">Saving Wallet</string> + <string name="status_wallet_unloaded">Wallet saved</string> <string name="status_wallet_connecting">Connecting …</string> <string name="status_working">Working on it …</string> <string name="prompt_password">Password for</string> <string name="bad_password">Bad password!</string> + <string name="bad_wallet">Wallet does not exists!</string> <string name="prompt_daemon_missing">Daemon address must be set!</string> - <string name="prompt_wrong_net">Daemon type does not fit to wallet!</string> + <string name="prompt_wrong_net">Wallet does not match selected net</string> <string name="warn_daemon_unavailable">Cannot connect to daemon! Try again.</string> <string name="panic">Something\'s wrong!</string> @@ -49,8 +51,11 @@ <string name="generate_buttonGenerate">Do it already!</string> <string name="generate_seed">Mnemonic Seed</string> <string name="generate_buttonAccept">I have noted this mnemonic seed\nNow, I want to loose all my money!</string> - <string name="generate_wallet_exists">Wallet exists!\nChoose another name</string> + <string name="generate_wallet_exists">Wallet exists! Choose another name</string> <string name="generate_wallet_created">Wallet created</string> - <string name="generate_wallet_create_failed">Wallet create failed</string> + <string name="generate_wallet_create_failed_1">Wallet create failed (1/2)</string> + <string name="generate_wallet_create_failed_2">Wallet create failed (2/2)</string> + <string name="generate_address_placeholder">9tDC52GsMjTNt4dpnRCwAF7ekVBkbkgkXGaMKTcSTpBhGpqkPX56jCNRydLq9oGjbbAQBsZhLfgmTKsntmxRd3TaJFYM2f8</string> + <string name="generate_mnemonic_placeholder">camp feline inflamed memoir afloat eight alerts females gutter cogs menu waveform gather tawny judge gusts yahoo doctor females biscuit alchemy reef agony austere camp</string> </resources> From fb62074d207e036dd4ffbb39b1d190cc1389baa1 Mon Sep 17 00:00:00 2001 From: m2049r <30435443+m2049r@users.noreply.github.com> Date: Wed, 16 Aug 2017 12:09:32 +0200 Subject: [PATCH 05/10] added app.iml to gitignore and updates android studio --- .idea/misc.xml | 2 +- app/.gitignore | 1 + app/app.iml | 148 ------------------------------------------------- 3 files changed, 2 insertions(+), 149 deletions(-) delete mode 100644 app/app.iml diff --git a/.idea/misc.xml b/.idea/misc.xml index 5d199810..fbb68289 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -37,7 +37,7 @@ <ConfirmationsSetting value="0" id="Add" /> <ConfirmationsSetting value="0" id="Remove" /> </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <output url="file://$PROJECT_DIR$/build/classes" /> </component> <component name="ProjectType"> diff --git a/app/.gitignore b/app/.gitignore index e7838274..b19c2073 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -1,2 +1,3 @@ .externalNativeBuild build +app.iml diff --git a/app/app.iml b/app/app.iml deleted file mode 100644 index 25bc19b1..00000000 --- a/app/app.iml +++ /dev/null @@ -1,148 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" type="JAVA_MODULE" version="4"> - <component name="FacetManager"> - <facet type="android-gradle" name="Android-Gradle"> - <configuration> - <option name="GRADLE_PROJECT_PATH" value=":app" /> - </configuration> - </facet> - <facet type="native-android-gradle" name="Native-Android-Gradle"> - <configuration> - <option name="SELECTED_BUILD_VARIANT" value="debug" /> - </configuration> - </facet> - <facet type="android" name="Android"> - <configuration> - <option name="SELECTED_BUILD_VARIANT" value="debug" /> - <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" /> - <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" /> - <afterSyncTasks> - <task>generateDebugSources</task> - </afterSyncTasks> - <option name="ALLOW_USER_CONFIGURATION" value="false" /> - <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" /> - <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" /> - <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" /> - <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" /> - </configuration> - </facet> - </component> - <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false"> - <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" /> - <output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" /> - <exclude-output /> - <content url="file://$MODULE_DIR$"> - <sourceFolder url="file://$MODULE_DIR$/src/main/cpp" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/src/testDebug/jni" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/src/test/jni" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/builds" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/cmake" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-classes" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-runtime-classes" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-verifier" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-resources" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-support" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/reload-dex" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/restart-dex" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/split-apk" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" /> - <excludeFolder url="file://$MODULE_DIR$/build/outputs" /> - <excludeFolder url="file://$MODULE_DIR$/build/tmp" /> - </content> - <orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" /> - <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="library" exported="" scope="TEST" name="runner-0.5" level="project" /> - <orderEntry type="library" exported="" scope="TEST" name="espresso-idling-resource-2.2.2" level="project" /> - <orderEntry type="library" exported="" name="constraint-layout-1.0.2" level="project" /> - <orderEntry type="library" exported="" scope="TEST" name="hamcrest-library-1.3" level="project" /> - <orderEntry type="library" exported="" name="transition-25.3.1" level="project" /> - <orderEntry type="library" exported="" scope="TEST" name="hamcrest-integration-1.3" level="project" /> - <orderEntry type="library" exported="" name="design-25.3.1" level="project" /> - <orderEntry type="library" exported="" name="support-core-ui-25.3.1" level="project" /> - <orderEntry type="library" exported="" name="cardview-v7-25.3.1" level="project" /> - <orderEntry type="library" exported="" scope="TEST" name="jsr305-2.0.1" level="project" /> - <orderEntry type="library" exported="" name="support-core-utils-25.3.1" level="project" /> - <orderEntry type="library" exported="" name="support-fragment-25.3.1" level="project" /> - <orderEntry type="library" exported="" scope="TEST" name="espresso-core-2.2.2" level="project" /> - <orderEntry type="library" exported="" scope="TEST" name="exposed-instrumentation-api-publish-0.5" level="project" /> - <orderEntry type="library" exported="" scope="TEST" name="rules-0.5" level="project" /> - <orderEntry type="library" exported="" name="constraint-layout-solver-1.0.2" level="project" /> - <orderEntry type="library" exported="" scope="TEST" name="javax.annotation-api-1.2" level="project" /> - <orderEntry type="library" exported="" scope="TEST" name="javax.inject-1" level="project" /> - <orderEntry type="library" exported="" scope="TEST" name="javawriter-2.1.1" level="project" /> - <orderEntry type="library" exported="" name="support-v4-25.3.1" level="project" /> - <orderEntry type="library" exported="" name="support-media-compat-25.3.1" level="project" /> - <orderEntry type="library" exported="" scope="TEST" name="hamcrest-core-1.3" level="project" /> - <orderEntry type="library" exported="" scope="TEST" name="junit-4.12" level="project" /> - <orderEntry type="library" exported="" name="recyclerview-v7-25.3.1" level="project" /> - <orderEntry type="library" exported="" name="support-annotations-25.3.1" level="project" /> - <orderEntry type="library" exported="" name="appcompat-v7-25.3.1" level="project" /> - <orderEntry type="library" exported="" name="support-vector-drawable-25.3.1" level="project" /> - <orderEntry type="library" exported="" name="support-compat-25.3.1" level="project" /> - <orderEntry type="library" exported="" name="animated-vector-drawable-25.3.1" level="project" /> - </component> -</module> \ No newline at end of file From 952fb3a7f123bfd581e9630dc3a94b9b97080c3f Mon Sep 17 00:00:00 2001 From: m2049r <30435443+m2049r@users.noreply.github.com> Date: Wed, 16 Aug 2017 12:39:00 +0200 Subject: [PATCH 06/10] deal with user closing fragment while doing wallet stuff --- app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java index b9d2c757..b9becc42 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java @@ -280,7 +280,8 @@ public class LoginActivity extends Activity newWallet.close(); runOnUiThread(new Runnable() { public void run() { - genFragment.showMnemonic(mnemonic); + if (genFragment.isAdded()) + genFragment.showMnemonic(mnemonic); } }); } @@ -290,6 +291,8 @@ public class LoginActivity extends Activity @Override public void onAccept(final String name, final String password) { + final GenerateFragment genFragment = (GenerateFragment) + getFragmentManager().findFragmentById(R.id.fragment_container); File newWalletFolder = new File(getStorageRoot(), ".new"); if (!newWalletFolder.isDirectory()) { Log.e(TAG, "New wallet dir " + newWalletFolder.getAbsolutePath() + "is not a directory"); @@ -324,7 +327,8 @@ public class LoginActivity extends Activity runOnUiThread(new Runnable() { public void run() { if (rc) { - getFragmentManager().popBackStack(); + if (genFragment.isAdded()) + getFragmentManager().popBackStack(); Toast.makeText(LoginActivity.this, getString(R.string.generate_wallet_created), Toast.LENGTH_SHORT).show(); } else { From bd598dedddca18b9769df1d014577ebcd261ac11 Mon Sep 17 00:00:00 2001 From: m2049r <30435443+m2049r@users.noreply.github.com> Date: Wed, 16 Aug 2017 19:39:53 +0200 Subject: [PATCH 07/10] separate wallet generation & confirmation all methods of recovery & creation implemented --- .idea/misc.xml | 2 +- .../m2049r/xmrwallet/GenerateFragment.java | 292 ++++++++++++++---- .../xmrwallet/GenerateReviewFragment.java | 116 +++++++ .../com/m2049r/xmrwallet/LoginActivity.java | 121 ++++++-- .../com/m2049r/xmrwallet/LoginFragment.java | 4 +- .../com/m2049r/xmrwallet/WalletFragment.java | 6 +- .../m2049r/xmrwallet/model/WalletManager.java | 17 +- .../xmrwallet/service/WalletService.java | 1 + app/src/main/res/layout/gen_fragment.xml | 171 +++++----- .../main/res/layout/gen_review_fragment.xml | 162 ++++++++++ app/src/main/res/layout/login_fragment.xml | 6 +- app/src/main/res/values/strings.xml | 31 +- 12 files changed, 747 insertions(+), 182 deletions(-) create mode 100644 app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java create mode 100644 app/src/main/res/layout/gen_review_fragment.xml diff --git a/.idea/misc.xml b/.idea/misc.xml index fbb68289..5d199810 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -37,7 +37,7 @@ <ConfirmationsSetting value="0" id="Add" /> <ConfirmationsSetting value="0" id="Remove" /> </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <output url="file://$PROJECT_DIR$/build/classes" /> </component> <component name="ProjectType"> diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java index 9fc842d3..56dfae2f 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2017 m2049r + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.m2049r.xmrwallet; import android.app.Fragment; @@ -5,6 +21,7 @@ import android.content.Context; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; +import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; @@ -24,15 +41,17 @@ import java.io.File; // TODO: somehow show which net we are generating for public class GenerateFragment extends Fragment { + static final String TAG = "GenerateFragment"; EditText etWalletName; EditText etWalletPassword; + EditText etWalletAddress; + EditText etWalletMnemonic; + LinearLayout llRestoreKeys; + EditText etWalletViewKey; + EditText etWalletSpendKey; + EditText etWalletRestoreHeight; Button bGenerate; - LinearLayout llAccept; - TextView tvWalletAddress; - TextView tvWalletMnemonic; - Button bAccept; - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -42,22 +61,27 @@ public class GenerateFragment extends Fragment { etWalletName = (EditText) view.findViewById(R.id.etWalletName); etWalletPassword = (EditText) view.findViewById(R.id.etWalletPassword); + etWalletMnemonic = (EditText) view.findViewById(R.id.etWalletMnemonic); + etWalletAddress = (EditText) view.findViewById(R.id.etWalletAddress); + llRestoreKeys = (LinearLayout) view.findViewById(R.id.llRestoreKeys); + etWalletViewKey = (EditText) view.findViewById(R.id.etWalletViewKey); + etWalletSpendKey = (EditText) view.findViewById(R.id.etWalletSpendKey); + etWalletRestoreHeight = (EditText) view.findViewById(R.id.etWalletRestoreHeight); bGenerate = (Button) view.findViewById(R.id.bGenerate); - llAccept = (LinearLayout) view.findViewById(R.id.llAccept); - tvWalletAddress = (TextView) view.findViewById(R.id.tvWalletAddress); - tvWalletMnemonic = (TextView) view.findViewById(R.id.tvWalletMnemonic); - bAccept = (Button) view.findViewById(R.id.bAccept); boolean testnet = WalletManager.getInstance().isTestNet(); - tvWalletMnemonic.setTextIsSelectable(testnet); + etWalletMnemonic.setTextIsSelectable(testnet); etWalletName.requestFocus(); Helper.showKeyboard(getActivity()); - setGenerateEnabled(); etWalletName.addTextChangedListener(new TextWatcher() { @Override public void afterTextChanged(Editable editable) { - showMnemonic(""); + if (etWalletName.length() > 0) { + bGenerate.setEnabled(true); + } else { + bGenerate.setEnabled(false); + } } @Override @@ -76,11 +100,11 @@ public class GenerateFragment extends Fragment { }); etWalletName.setOnEditorActionListener(new TextView.OnEditorActionListener() { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) { + if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) { if (etWalletName.length() > 0) { etWalletPassword.requestFocus(); } // otherwise ignore - return false; + return true; } return false; } @@ -92,10 +116,52 @@ public class GenerateFragment extends Fragment { Helper.showKeyboard(getActivity()); } }); - etWalletPassword.addTextChangedListener(new TextWatcher() { + etWalletPassword.setOnEditorActionListener(new TextView.OnEditorActionListener() { + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) { + etWalletMnemonic.requestFocus(); + return true; + } + return false; + } + }); + + etWalletMnemonic.setOnEditorActionListener(new TextView.OnEditorActionListener() { + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) { + if (etWalletMnemonic.length() == 0) { + etWalletAddress.requestFocus(); + } else if (mnemonicOk()) { + etWalletRestoreHeight.requestFocus(); + } else { + Toast.makeText(getActivity(), getString(R.string.generate_check_mnemonic), Toast.LENGTH_LONG).show(); + } + return true; + } + return false; + } + }); + etWalletMnemonic.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Helper.showKeyboard(getActivity()); + } + }); + etWalletMnemonic.addTextChangedListener(new TextWatcher() { @Override public void afterTextChanged(Editable editable) { - showMnemonic(""); + if (etWalletMnemonic.length() > 0) { + etWalletRestoreHeight.setVisibility(View.VISIBLE); + etWalletAddress.setVisibility(View.INVISIBLE); + } else { + etWalletAddress.setVisibility(View.VISIBLE); + if (etWalletAddress.length() == 0) { + etWalletRestoreHeight.setVisibility(View.INVISIBLE); + } else { + etWalletRestoreHeight.setVisibility(View.VISIBLE); + } + + } } @Override @@ -106,12 +172,103 @@ public class GenerateFragment extends Fragment { public void onTextChanged(CharSequence s, int start, int before, int count) { } }); - etWalletPassword.setOnEditorActionListener(new TextView.OnEditorActionListener() { + + etWalletAddress.setOnEditorActionListener(new TextView.OnEditorActionListener() { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) { + if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) { + if (etWalletAddress.length() == 0) { + Helper.hideKeyboard(getActivity()); + generateWallet(); + } else if (addressOk()) { + etWalletViewKey.requestFocus(); + } else { + Toast.makeText(getActivity(), getString(R.string.generate_check_address), Toast.LENGTH_LONG).show(); + } + return true; + } + return false; + } + }); + etWalletAddress.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Helper.showKeyboard(getActivity()); + } + }); + etWalletAddress.addTextChangedListener(new TextWatcher() { + @Override + public void afterTextChanged(Editable editable) { + if (etWalletAddress.length() > 0) { + llRestoreKeys.setVisibility(View.VISIBLE); + etWalletMnemonic.setVisibility(View.INVISIBLE); + etWalletRestoreHeight.setVisibility(View.VISIBLE); + } else { + llRestoreKeys.setVisibility(View.INVISIBLE); + etWalletMnemonic.setVisibility(View.VISIBLE); + if (etWalletMnemonic.length() == 0) { + etWalletRestoreHeight.setVisibility(View.INVISIBLE); + } else { + etWalletRestoreHeight.setVisibility(View.VISIBLE); + } + } + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + }); + + etWalletViewKey.setOnEditorActionListener(new TextView.OnEditorActionListener() { + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) { + if (viewKeyOk()) { + etWalletSpendKey.requestFocus(); + } else { + Toast.makeText(getActivity(), getString(R.string.generate_check_key), Toast.LENGTH_LONG).show(); + } + return true; + } + return false; + } + }); + etWalletViewKey.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Helper.showKeyboard(getActivity()); + } + }); + + etWalletSpendKey.setOnEditorActionListener(new TextView.OnEditorActionListener() { + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) { + if (spendKeyOk()) { + Helper.hideKeyboard(getActivity()); + generateWallet(); + } else { + Toast.makeText(getActivity(), getString(R.string.generate_check_key), Toast.LENGTH_LONG).show(); + } + return true; + } + return false; + } + }); + etWalletSpendKey.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Helper.showKeyboard(getActivity()); + } + }); + + etWalletRestoreHeight.setOnEditorActionListener(new TextView.OnEditorActionListener() { + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) { Helper.hideKeyboard(getActivity()); generateWallet(); - return false; + return true; } return false; } @@ -125,19 +282,29 @@ public class GenerateFragment extends Fragment { } }); - bAccept.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - acceptWallet(); - } - }); - - bAccept.setEnabled(false); - llAccept.setVisibility(View.GONE); - return view; } + private boolean mnemonicOk() { + String seed = etWalletMnemonic.getText().toString(); + return (seed.split("\\s").length == 25); // 25 words + } + + private boolean addressOk() { + String address = etWalletAddress.getText().toString(); + return ((address.length() == 95) && ("49A".indexOf(address.charAt(0)) >= 0)); + } + + private boolean viewKeyOk() { + String viewKey = etWalletViewKey.getText().toString(); + return (viewKey.length() == 64) && (viewKey.matches("^[0-9a-fA-F]+$")); + } + + private boolean spendKeyOk() { + String spendKey = etWalletSpendKey.getText().toString(); + return ((spendKey.length() == 0) || ((spendKey.length() == 64) && (spendKey.matches("^[0-9a-fA-F]+$")))); + } + private void generateWallet() { String name = etWalletName.getText().toString(); if (name.length() == 0) return; @@ -148,43 +315,56 @@ public class GenerateFragment extends Fragment { return; } String password = etWalletPassword.getText().toString(); - bGenerate.setEnabled(false); - activityCallback.onGenerate(name, password); - } - private void acceptWallet() { - String name = etWalletName.getText().toString(); - String password = etWalletPassword.getText().toString(); - bAccept.setEnabled(false); - activityCallback.onAccept(name, password); - } + String seed = etWalletMnemonic.getText().toString(); + String address = etWalletAddress.getText().toString(); - private void setGenerateEnabled() { - bGenerate.setEnabled(etWalletName.length() > 0); - } - - public void showMnemonic(String mnemonic) { - setGenerateEnabled(); - if (mnemonic.length() > 0) { - tvWalletMnemonic.setText(mnemonic); - bAccept.setEnabled(true); - llAccept.setVisibility(View.VISIBLE); - } else { - if (llAccept.getVisibility() != View.GONE) { - tvWalletMnemonic.setText(getActivity().getString(R.string.generate_seed)); - bAccept.setEnabled(false); - llAccept.setVisibility(View.GONE); - } + long height; + try { + height = Long.parseLong(etWalletRestoreHeight.getText().toString()); + } catch (NumberFormatException ex) { + Log.e(TAG, "Cannot parse " + etWalletRestoreHeight.getText().toString()); + Log.e(TAG, ex.getLocalizedMessage()); + height = 0; // Keep calm and carry on! } + + // figure out how we want to create this wallet + // A. from scratch + if ((seed.length() == 0) && (address.length() == 0)) { + bGenerate.setVisibility(View.INVISIBLE); + activityCallback.onGenerate(name, password); + } else + // B. from seed + if (mnemonicOk()) { + bGenerate.setVisibility(View.INVISIBLE); + activityCallback.onGenerate(name, password, seed, height); + } else + // C. from keys + if (addressOk() && viewKeyOk() && (spendKeyOk())) { + String viewKey = etWalletViewKey.getText().toString(); + String spendKey = etWalletSpendKey.getText().toString(); + bGenerate.setVisibility(View.INVISIBLE); + activityCallback.onGenerate(name, password, address, viewKey, spendKey, height); + } else + // D. none of the above :) + { + Toast.makeText(getActivity(), getString(R.string.generate_check_something), Toast.LENGTH_LONG).show(); + } + } + + public void walletGenerateError() { + bGenerate.setEnabled(etWalletName.length() > 0); + bGenerate.setVisibility(View.VISIBLE); } GenerateFragment.Listener activityCallback; - // Container Activity must implement this interface public interface Listener { void onGenerate(String name, String password); - void onAccept(String name, String password); + void onGenerate(String name, String password, String seed, long height); + + void onGenerate(String name, String password, String address, String viewKey, String spendKey, long height); File getStorageRoot(); } diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java new file mode 100644 index 00000000..38be477a --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017 m2049r + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.m2049r.xmrwallet; + +import android.app.Fragment; +import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +import com.m2049r.xmrwallet.model.WalletManager; + +import java.text.NumberFormat; + +// TODO: somehow show which net we are generating for + +public class GenerateReviewFragment extends Fragment { + static final String TAG = "GenerateReviewFragment"; + + TextView tvWalletName; + TextView tvWalletPassword; + TextView tvWalletAddress; + TextView tvWalletMnemonic; + TextView tvWalletViewKey; + TextView tvWalletSpendKey; + Button bAccept; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View view = inflater.inflate(R.layout.gen_review_fragment, container, false); + + tvWalletName = (TextView) view.findViewById(R.id.tvWalletName); + tvWalletPassword = (TextView) view.findViewById(R.id.tvWalletPassword); + tvWalletAddress = (TextView) view.findViewById(R.id.tvWalletAddress); + tvWalletViewKey = (TextView) view.findViewById(R.id.tvWalletViewKey); + tvWalletSpendKey = (TextView) view.findViewById(R.id.tvWalletSpendKey); + tvWalletMnemonic = (TextView) view.findViewById(R.id.tvWalletMnemonic); + + bAccept = (Button) view.findViewById(R.id.bAccept); + + boolean testnet = WalletManager.getInstance().isTestNet(); + tvWalletMnemonic.setTextIsSelectable(testnet); + + bAccept.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + acceptWallet(); + } + }); + + showDetails(); + return view; + } + + private void acceptWallet() { + String name = tvWalletName.getText().toString(); + String password = tvWalletPassword.getText().toString(); + bAccept.setEnabled(false); + activityCallback.onAccept(name, password); + } + + public void showDetails() { + Bundle b = getArguments(); + String name = b.getString("name"); + String password = b.getString("password"); + String address = b.getString("address"); + String seed = b.getString("seed"); + String view = b.getString("viewkey"); + String spend = b.getString("spendkey"); + long height = b.getLong("restoreHeight"); + tvWalletName.setText(name); + tvWalletPassword.setText(password); + tvWalletAddress.setText(address); + tvWalletMnemonic.setText(seed); + tvWalletViewKey.setText(view); + tvWalletSpendKey.setText(spend); + NumberFormat formatter = NumberFormat.getInstance(); + bAccept.setEnabled(true); + } + + GenerateReviewFragment.Listener activityCallback; + + public interface Listener { + void onAccept(String name, String password); + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof GenerateReviewFragment.Listener) { + this.activityCallback = (GenerateReviewFragment.Listener) context; + } else { + throw new ClassCastException(context.toString() + + " must implement Listener"); + } + } +} diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java index b9becc42..0d3d76dc 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java @@ -19,6 +19,7 @@ package com.m2049r.xmrwallet; import android.app.Activity; import android.app.AlertDialog; import android.app.Fragment; +import android.app.FragmentManager; import android.app.FragmentTransaction; import android.content.Context; import android.content.DialogInterface; @@ -31,9 +32,7 @@ import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; -import android.view.WindowManager; import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; @@ -46,7 +45,7 @@ import com.m2049r.xmrwallet.util.Helper; import java.io.File; public class LoginActivity extends Activity - implements LoginFragment.Listener, GenerateFragment.Listener { + implements LoginFragment.Listener, GenerateFragment.Listener, GenerateReviewFragment.Listener { static final String TAG = "LoginActivity"; static final int DAEMON_TIMEOUT = 500; // deamon must respond in 500ms @@ -219,14 +218,22 @@ public class LoginActivity extends Activity } void startGenerateFragment() { - replaceFragment(new GenerateFragment()); + replaceFragment(new GenerateFragment(), "gen", null); Log.d(TAG, "GenerateFragment placed"); } - void replaceFragment(Fragment newFragment) { + void startReviewFragment(Bundle extras) { + replaceFragment(new GenerateReviewFragment(), null, extras); + Log.d(TAG, "GenerateReviewFragment placed"); + } + + void replaceFragment(Fragment newFragment, String name, Bundle extras) { + if (extras != null) { + newFragment.setArguments(extras); + } FragmentTransaction transaction = getFragmentManager().beginTransaction(); transaction.replace(R.id.fragment_container, newFragment); - transaction.addToBackStack(null); + transaction.addToBackStack(name); transaction.commit(); } @@ -235,33 +242,33 @@ public class LoginActivity extends Activity ////////////////////////////////////////// static final String MNEMONIC_LANGUAGE = "English"; // see mnemonics/electrum-words.cpp for more - @Override - public void onGenerate(final String name, final String password) { + public void createWallet(final String name, final String password, final WalletCreator walletCreator) { final GenerateFragment genFragment = (GenerateFragment) getFragmentManager().findFragmentById(R.id.fragment_container); File newWalletFolder = new File(getStorageRoot(), ".new"); if (!newWalletFolder.exists()) { if (!newWalletFolder.mkdir()) { Log.e(TAG, "Cannot create new wallet dir " + newWalletFolder.getAbsolutePath()); - genFragment.showMnemonic(""); + genFragment.walletGenerateError(); return; } } if (!newWalletFolder.isDirectory()) { Log.e(TAG, "New wallet dir " + newWalletFolder.getAbsolutePath() + "is not a directory"); - genFragment.showMnemonic(""); + genFragment.walletGenerateError(); return; } - File cache = new File(newWalletFolder, name); - cache.delete(); - File keys = new File(newWalletFolder, name + ".keys"); - keys.delete(); - File address = new File(newWalletFolder, name + ".address.txt"); - address.delete(); + File cacheFile = new File(newWalletFolder, name); + cacheFile.delete(); + File keysFile = new File(newWalletFolder, name + ".keys"); + keysFile.delete(); + final File addressFile = new File(newWalletFolder, name + ".address.txt"); + addressFile.delete(); - if (cache.exists() || keys.exists() || address.exists()) { - Log.e(TAG, "Cannot remove all old wallet files: " + cache.getAbsolutePath()); - genFragment.showMnemonic(""); + if (cacheFile.exists() || keysFile.exists() || addressFile.exists()) { + Log.e(TAG, "Cannot remove all old wallet files: " + cacheFile.getAbsolutePath()); + genFragment.walletGenerateError(); + ; return; } @@ -271,17 +278,25 @@ public class LoginActivity extends Activity @Override public void run() { Log.d(TAG, "creating wallet " + newWalletPath); - Wallet newWallet = WalletManager.getInstance() - .createWallet(newWalletPath, password, MNEMONIC_LANGUAGE); - Log.d(TAG, "wallet created"); - Log.d(TAG, "Created " + newWallet.getAddress()); - Log.d(TAG, "Seed " + newWallet.getSeed() + "."); - final String mnemonic = newWallet.getSeed(); + Wallet newWallet = walletCreator.createWallet(newWalletPath, password); + final String seed = newWallet.getSeed(); + final String address = newWallet.getAddress(); + final String view = newWallet.getSecretViewKey(); + final long height = newWallet.getBlockChainHeight(); + final String spend = "not available - use seed for recovery"; //TODO newWallet.close(); + Log.d(TAG, "Created " + address); runOnUiThread(new Runnable() { public void run() { - if (genFragment.isAdded()) - genFragment.showMnemonic(mnemonic); + Bundle b = new Bundle(); + b.putString("name", name); + b.putString("password", password); + b.putString("seed", seed); + b.putString("address", address); + b.putString("viewkey", view); + b.putString("spendkey", spend); + b.putLong("restoreHeight", height); + startReviewFragment(b); } }); } @@ -289,10 +304,54 @@ public class LoginActivity extends Activity , "CreateWallet", MoneroHandlerThread.THREAD_STACK_SIZE).start(); } + interface WalletCreator { + Wallet createWallet(String path, String password); + } + + @Override + public void onGenerate(String name, String password) { + createWallet(name, password, + new WalletCreator() { + public Wallet createWallet(String path, String password) { + return WalletManager.getInstance() + .createWallet(path, password, MNEMONIC_LANGUAGE); + + } + }); + } + + @Override + public void onGenerate(String name, String password, final String seed, final long restoreHeight) { + createWallet(name, password, + new WalletCreator() { + public Wallet createWallet(String path, String password) { + Wallet newWallet = WalletManager.getInstance().recoveryWallet(path, seed, restoreHeight); + newWallet.setPassword(password); + newWallet.store(); + return newWallet; + } + }); + } + + @Override + public void onGenerate(String name, String password, + final String address, final String viewKey, final String spendKey, final long restoreHeight) { + createWallet(name, password, + new WalletCreator() { + public Wallet createWallet(String path, String password) { + Wallet newWallet = WalletManager.getInstance() + .createWalletFromKeys(path, MNEMONIC_LANGUAGE, restoreHeight, + address, viewKey, spendKey); + newWallet.setPassword(password); + newWallet.store(); + return newWallet; + } + }); + } + + @Override public void onAccept(final String name, final String password) { - final GenerateFragment genFragment = (GenerateFragment) - getFragmentManager().findFragmentById(R.id.fragment_container); File newWalletFolder = new File(getStorageRoot(), ".new"); if (!newWalletFolder.isDirectory()) { Log.e(TAG, "New wallet dir " + newWalletFolder.getAbsolutePath() + "is not a directory"); @@ -327,8 +386,8 @@ public class LoginActivity extends Activity runOnUiThread(new Runnable() { public void run() { if (rc) { - if (genFragment.isAdded()) - getFragmentManager().popBackStack(); + getFragmentManager().popBackStack("gen", + FragmentManager.POP_BACK_STACK_INCLUSIVE); Toast.makeText(LoginActivity.this, getString(R.string.generate_wallet_created), Toast.LENGTH_SHORT).show(); } else { diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java index 3ab58db9..ae05a405 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java @@ -193,7 +193,7 @@ public class LoginFragment extends Fragment { displayedList.clear(); String x = isMainNet() ? "4" : "9A"; for (String s : walletList) { - Log.d(TAG, "filtering " + s); + // Log.d(TAG, "filtering " + s); if (x.indexOf(s.charAt(1)) >= 0) displayedList.add(s); } displayedList.add(WALLETNAME_PREAMBLE + getString(R.string.generate_title)); @@ -206,7 +206,7 @@ public class LoginFragment extends Fragment { walletList.clear(); for (WalletManager.WalletInfo walletInfo : walletInfos) { - Log.d(TAG, walletInfo.address); + // Log.d(TAG, walletInfo.address); String displayAddress = walletInfo.address; if (displayAddress.length() == 95) { displayAddress = walletInfo.address.substring(0, 6); diff --git a/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java b/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java index ee4a636d..c708b3c8 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java @@ -37,11 +37,13 @@ import com.m2049r.xmrwallet.layout.TransactionInfoAdapter; import com.m2049r.xmrwallet.model.TransactionInfo; import com.m2049r.xmrwallet.model.Wallet; +import java.text.NumberFormat; import java.util.List; public class WalletFragment extends Fragment implements TransactionInfoAdapter.OnInteractionListener { private static final String TAG = "WalletFragment"; private TransactionInfoAdapter adapter; + private NumberFormat formatter = NumberFormat.getInstance(); @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -171,7 +173,7 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O long daemonHeight = activityCallback.getDaemonHeight(); if (!wallet.isSynchronized()) { long n = daemonHeight - wallet.getBlockChainHeight(); - sync = n + " " + getString(R.string.status_remaining); + sync = formatter.format(n) + " " + getString(R.string.status_remaining); if (firstBlock == 0) { firstBlock = wallet.getBlockChainHeight(); } @@ -180,7 +182,7 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O if (x == 0) x = -1; onProgress(x); } else { - sync = getString(R.string.status_synced) + ": " + wallet.getBlockChainHeight(); + sync = getString(R.string.status_synced) + ": " + formatter.format(wallet.getBlockChainHeight()); } } String net = (wallet.isTestNet() ? getString(R.string.connect_testnet) : getString(R.string.connect_mainnet)); diff --git a/app/src/main/java/com/m2049r/xmrwallet/model/WalletManager.java b/app/src/main/java/com/m2049r/xmrwallet/model/WalletManager.java index 54782cf2..357027bb 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/model/WalletManager.java +++ b/app/src/main/java/com/m2049r/xmrwallet/model/WalletManager.java @@ -78,6 +78,8 @@ public class WalletManager { return wallet; } + private native long createWalletJ(String path, String password, String language, boolean isTestNet); + public Wallet openWallet(String path, String password) { long walletHandle = openWalletJ(path, password, isTestNet()); Wallet wallet = new Wallet(walletHandle); @@ -85,6 +87,8 @@ public class WalletManager { return wallet; } + private native long openWalletJ(String path, String password, boolean isTestNet); + public Wallet recoveryWallet(String path, String mnemonic) { Wallet wallet = recoveryWallet(path, mnemonic, 0); manageWallet(wallet.getName(), wallet); @@ -98,12 +102,17 @@ public class WalletManager { return wallet; } - private native long createWalletJ(String path, String password, String language, boolean isTestNet); - - private native long openWalletJ(String path, String password, boolean isTestNet); - private native long recoveryWalletJ(String path, String mnemonic, boolean isTestNet, long restoreHeight); + public Wallet createWalletFromKeys(String path, String language, long restoreHeight, + String addressString, String viewKeyString, String spendKeyString) { + long walletHandle = createWalletFromKeysJ(path, language, isTestNet(), restoreHeight, + addressString, viewKeyString, spendKeyString); + Wallet wallet = new Wallet(walletHandle); + manageWallet(wallet.getName(), wallet); + return wallet; + } + private native long createWalletFromKeysJ(String path, String language, boolean isTestNet, long restoreHeight, diff --git a/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java b/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java index b40f3a8e..f67ab9b8 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java +++ b/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java @@ -419,6 +419,7 @@ public class WalletService extends Service { WalletManager.getInstance().close(wallet); // TODO close() failed? wallet = null; // TODO what do we do with the progress?? + // TODO tell the activity this failed } } return wallet; diff --git a/app/src/main/res/layout/gen_fragment.xml b/app/src/main/res/layout/gen_fragment.xml index e0ed6a90..2fcf1158 100644 --- a/app/src/main/res/layout/gen_fragment.xml +++ b/app/src/main/res/layout/gen_fragment.xml @@ -2,108 +2,121 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical"> + android:gravity="center_horizontal" + android:orientation="vertical" + android:paddingBottom="@dimen/activity_vertical_margin" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin"> - <TextView - android:layout_width="match_parent" + <LinearLayout + android:layout_width="fill_parent" android:layout_height="wrap_content" - android:layout_marginBottom="16dp" - android:gravity="center" - android:text="@string/generate_title" - android:textSize="30sp" /> + android:orientation="horizontal" + android:weightSum="2"> + + <EditText + android:id="@+id/etWalletName" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:hint="@string/generate_name_hint" + android:imeOptions="actionNext" + android:inputType="text" + android:maxLines="1" + android:textAlignment="center" + android:textSize="16sp" /> + + <EditText + android:id="@+id/etWalletPassword" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:hint="@string/generate_password_hint" + android:imeOptions="actionNext" + android:inputType="text" + android:textAlignment="center" + android:textSize="16sp" /> + </LinearLayout> <EditText - android:id="@+id/etWalletName" + android:id="@+id/etWalletMnemonic" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="16dp" - android:layout_marginEnd="8dp" - android:layout_marginStart="8dp" - android:layout_marginTop="8dp" - android:backgroundTint="@color/colorPrimary" - android:focusable="true" - android:gravity="center" - android:hint="@string/generate_name" - android:imeOptions="actionDone" + android:hint="@string/generate_mnemonic_hint" + android:imeOptions="actionNext" android:inputType="text" - android:maxLines="1" - android:textIsSelectable="true" - android:textSize="24sp" /> + android:scrollHorizontally="false" + android:textAlignment="center" + android:textSize="16sp" /> <EditText - android:id="@+id/etWalletPassword" + android:id="@+id/etWalletAddress" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="16dp" - android:layout_marginEnd="8dp" - android:layout_marginStart="8dp" - android:layout_marginTop="8dp" - android:backgroundTint="@color/colorPrimary" - android:focusable="true" - android:gravity="center" - android:hint="@string/generate_password" - android:imeOptions="actionDone" + android:hint="@string/generate_address_hint" + android:imeOptions="actionNext" android:inputType="text" - android:maxLines="1" - android:textIsSelectable="true" - android:textSize="24sp" /> + android:textAlignment="center" + android:textSize="16sp" /> + + <LinearLayout + android:id="@+id/llRestoreKeys" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center" + android:orientation="vertical" + android:visibility="invisible"> + + <EditText + android:id="@+id/etWalletViewKey" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center" + android:hint="@string/generate_viewkey_hint" + android:imeOptions="actionNext" + android:inputType="text" + android:textAlignment="center" + android:textSize="16sp" /> + + <EditText + android:id="@+id/etWalletSpendKey" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/generate_spendkey_hint" + android:imeOptions="actionNext" + android:inputType="text" + android:textAlignment="center" + android:textSize="16sp" /> + </LinearLayout> + + <EditText + android:id="@+id/etWalletRestoreHeight" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/generate_restoreheight_hint" + android:imeOptions="actionNext" + android:inputType="number" + android:textAlignment="center" + android:textSize="16sp" + android:visibility="invisible" /> <Button android:id="@+id/bGenerate" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="8dp" - android:layout_marginLeft="16dp" - android:layout_marginRight="16dp" android:layout_marginTop="8dp" android:background="@color/colorPrimary" + android:enabled="false" android:text="@string/generate_buttonGenerate" /> - <LinearLayout - android:id="@+id/llAccept" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:orientation="vertical" - android:visibility="visible"> - - <TextView - android:id="@+id/tvWalletAddress" - android:textIsSelectable="true" - android:selectAllOnFocus="true" + <!-- <Button + android:id="@+id/bReset" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="8dp" - android:layout_marginEnd="8dp" - android:layout_marginStart="8dp" - android:layout_marginTop="8dp" - android:text="@string/generate_address_placeholder" - android:textAlignment="center" - android:textColor="@color/menu_background_color" - android:textSize="12sp" /> - - <TextView - android:id="@+id/tvWalletMnemonic" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginBottom="8dp" - android:layout_marginEnd="8dp" - android:layout_marginStart="8dp" - android:layout_marginTop="8dp" - android:text="@string/generate_mnemonic_placeholder" - android:textAlignment="center" - android:textColor="@color/menu_background_color" - android:textSize="20sp" /> - - <Button - android:id="@+id/bAccept" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginBottom="16dp" - android:layout_marginLeft="16dp" - android:layout_marginRight="16dp" - android:layout_marginTop="8dp" + android:layout_marginTop="16dp" android:background="@color/colorPrimary" - android:text="@string/generate_buttonAccept" /> - </LinearLayout> + android:text="@string/generate_button_reset" /> + --> </LinearLayout> diff --git a/app/src/main/res/layout/gen_review_fragment.xml b/app/src/main/res/layout/gen_review_fragment.xml new file mode 100644 index 00000000..816143ab --- /dev/null +++ b/app/src/main/res/layout/gen_review_fragment.xml @@ -0,0 +1,162 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center_horizontal" + android:orientation="vertical" + android:paddingBottom="@dimen/activity_vertical_margin" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin"> + + <LinearLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:weightSum="2"> + + <TextView + android:id="@+id/tvWalletLabel" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="@string/generate_wallet_label" + android:textAlignment="center" + android:textColor="@color/colorAccent" + android:textSize="16sp" /> + + <TextView + android:id="@+id/tvWalletPasswordLabel" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="@string/generate_password_label" + android:textAlignment="center" + android:textColor="@color/colorAccent" + android:textSize="16sp" /> + </LinearLayout> + + <LinearLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:weightSum="2"> + + <TextView + android:id="@+id/tvWalletName" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginBottom="8dp" + android:layout_weight="1" + android:text="@string/generate_name_hint" + android:textAlignment="center" + android:textColor="@color/colorPrimaryDark" + android:textSize="16sp" /> + + <TextView + android:id="@+id/tvWalletPassword" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginBottom="8dp" + android:layout_weight="1" + android:text="@string/generate_password_hint" + android:textAlignment="center" + android:textColor="@color/colorPrimaryDark" + android:textSize="16sp" /> + </LinearLayout> + + <TextView + android:id="@+id/tvWalletMnemonicLabel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/generate_mnemonic_label" + android:textAlignment="center" + android:textColor="@color/colorAccent" + android:textSize="16sp" /> + + <TextView + android:id="@+id/tvWalletMnemonic" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="8dp" + android:text="@string/generate_mnemonic_placeholder" + android:textAlignment="center" + android:textColor="@color/colorPrimaryDark" + android:textSize="16sp" /> + + <TextView + android:id="@+id/tvWalletAddressLabel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/generate_address_label" + android:textAlignment="center" + android:textColor="@color/colorAccent" + android:textSize="16sp" /> + + <TextView + android:id="@+id/tvWalletAddress" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="8dp" + android:layout_marginTop="8dp" + android:selectAllOnFocus="true" + android:text="@string/generate_address_placeholder" + android:textAlignment="center" + android:textColor="@color/colorPrimaryDark" + android:textIsSelectable="true" + android:textSize="16sp" /> + + <TextView + android:id="@+id/tvWalletViewKeyLabel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/generate_viewkey_label" + android:textAlignment="center" + android:textColor="@color/colorAccent" + android:textSize="16sp" /> + + <TextView + android:id="@+id/tvWalletViewKey" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="8dp" + android:layout_marginTop="8dp" + android:selectAllOnFocus="true" + android:text="@string/generate_viewkey_placeholder" + android:textAlignment="center" + android:textColor="@color/colorPrimaryDark" + android:textIsSelectable="true" + android:textSize="16sp" /> + + <TextView + android:id="@+id/tvWalletSpendKeyLabel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/generate_spendkey_label" + android:textAlignment="center" + android:textColor="@color/colorAccent" + android:textSize="16sp" /> + + <TextView + android:id="@+id/tvWalletSpendKey" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="8dp" + android:layout_marginTop="8dp" + android:selectAllOnFocus="true" + android:text="@string/generate_spendkey_placeholder" + android:textAlignment="center" + android:textColor="@color/colorPrimaryDark" + android:textIsSelectable="true" + android:textSize="16sp" /> + + <Button + android:id="@+id/bAccept" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="8dp" + android:layout_marginTop="8dp" + android:background="@color/colorPrimary" + android:text="@string/generate_button_accept" /> + +</LinearLayout> diff --git a/app/src/main/res/layout/login_fragment.xml b/app/src/main/res/layout/login_fragment.xml index 5090c92e..8ae9995d 100644 --- a/app/src/main/res/layout/login_fragment.xml +++ b/app/src/main/res/layout/login_fragment.xml @@ -1,5 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_horizontal" @@ -7,12 +7,10 @@ android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" - android:paddingTop="@dimen/activity_vertical_margin" - tools:context="com.m2049r.xmrwallet.LoginActivity"> + android:paddingTop="@dimen/activity_vertical_margin"> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content"> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 09cfd3be..6f27b22c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -46,16 +46,41 @@ <string name="message_strorage_not_permitted">We really need those External Storage permissions!</string> <string name="generate_title">Generate Wallet</string> - <string name="generate_name">Wallet Name</string> - <string name="generate_password">Wallet Password</string> + <string name="generate_name_hint">Wallet Name</string> + <string name="generate_password_hint">Wallet Password</string> <string name="generate_buttonGenerate">Do it already!</string> <string name="generate_seed">Mnemonic Seed</string> - <string name="generate_buttonAccept">I have noted this mnemonic seed\nNow, I want to loose all my money!</string> + <string name="generate_button_accept">I have noted the mnemonic seed\nNow, I want to loose all my money!</string> + <string name="generate_button_reset">I\'m confused - Let me start again!</string> + <string name="generate_wallet_exists">Wallet exists! Choose another name</string> <string name="generate_wallet_created">Wallet created</string> <string name="generate_wallet_create_failed_1">Wallet create failed (1/2)</string> <string name="generate_wallet_create_failed_2">Wallet create failed (2/2)</string> <string name="generate_address_placeholder">9tDC52GsMjTNt4dpnRCwAF7ekVBkbkgkXGaMKTcSTpBhGpqkPX56jCNRydLq9oGjbbAQBsZhLfgmTKsntmxRd3TaJFYM2f8</string> + <string name="generate_viewkey_placeholder">e2b99f4cc3d644774c4b118db05f8aa9967583a01ca4d47058c3860af10bd306</string> + <string name="generate_spendkey_placeholder">300a54208ab0a638a8407a12e3de946da76f5a9ded303338452332ec7755210d</string> <string name="generate_mnemonic_placeholder">camp feline inflamed memoir afloat eight alerts females gutter cogs menu waveform gather tawny judge gusts yahoo doctor females biscuit alchemy reef agony austere camp</string> + <string name="generate_restoreheight_placeholder">1307882</string> + + <string name="generate_address_hint">Public Address (optional)</string> + <string name="generate_viewkey_hint">View Key</string> + <string name="generate_spendkey_hint">Spend Key (optional)</string> + <string name="generate_mnemonic_hint">Mnemonic Seed (optional)</string> + <string name="generate_restoreheight_hint">Restore Height (optional)</string> + + <string name="generate_wallet_label">Wallet</string> + <string name="generate_password_label">Password</string> + <string name="generate_address_label">Public Address</string> + <string name="generate_viewkey_label">View Key</string> + <string name="generate_spendkey_label">Spend Key</string> + <string name="generate_mnemonic_label">Mnemonic Seed</string> + <string name="generate_restoreheight_label">Restore Height:</string> + + <string name="generate_check_keys">Check your keys!</string> + <string name="generate_check_key">Check your key!</string> + <string name="generate_check_address">Check your address!</string> + <string name="generate_check_mnemonic">Check your mnemonic seed!</string> + <string name="generate_check_something">Check your entry!</string> </resources> From afc45e1cbca6799cbe2060905bb7e977ad8a6ad4 Mon Sep 17 00:00:00 2001 From: m2049r <30435443+m2049r@users.noreply.github.com> Date: Wed, 16 Aug 2017 20:59:55 +0200 Subject: [PATCH 08/10] UI tweaks --- .../com/m2049r/xmrwallet/GenerateFragment.java | 1 + app/src/main/res/layout/gen_fragment.xml | 10 ---------- app/src/main/res/layout/gen_review_fragment.xml | 16 +++++----------- 3 files changed, 6 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java index 56dfae2f..755b1ea2 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java @@ -20,6 +20,7 @@ import android.app.Fragment; import android.content.Context; import android.os.Bundle; import android.text.Editable; +import android.text.InputType; import android.text.TextWatcher; import android.util.Log; import android.view.KeyEvent; diff --git a/app/src/main/res/layout/gen_fragment.xml b/app/src/main/res/layout/gen_fragment.xml index 2fcf1158..101e94e8 100644 --- a/app/src/main/res/layout/gen_fragment.xml +++ b/app/src/main/res/layout/gen_fragment.xml @@ -46,7 +46,6 @@ android:hint="@string/generate_mnemonic_hint" android:imeOptions="actionNext" android:inputType="text" - android:scrollHorizontally="false" android:textAlignment="center" android:textSize="16sp" /> @@ -72,7 +71,6 @@ android:id="@+id/etWalletViewKey" android:layout_width="match_parent" android:layout_height="wrap_content" - android:gravity="center" android:hint="@string/generate_viewkey_hint" android:imeOptions="actionNext" android:inputType="text" @@ -111,12 +109,4 @@ android:enabled="false" android:text="@string/generate_buttonGenerate" /> - <!-- <Button - android:id="@+id/bReset" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="16dp" - android:background="@color/colorPrimary" - android:text="@string/generate_button_reset" /> - --> </LinearLayout> diff --git a/app/src/main/res/layout/gen_review_fragment.xml b/app/src/main/res/layout/gen_review_fragment.xml index 816143ab..a41a8b7d 100644 --- a/app/src/main/res/layout/gen_review_fragment.xml +++ b/app/src/main/res/layout/gen_review_fragment.xml @@ -40,13 +40,13 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_marginBottom="8dp" android:weightSum="2"> <TextView android:id="@+id/tvWalletName" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginBottom="8dp" android:layout_weight="1" android:text="@string/generate_name_hint" android:textAlignment="center" @@ -57,7 +57,6 @@ android:id="@+id/tvWalletPassword" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginBottom="8dp" android:layout_weight="1" android:text="@string/generate_password_hint" android:textAlignment="center" @@ -78,7 +77,6 @@ android:id="@+id/tvWalletMnemonic" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="8dp" android:text="@string/generate_mnemonic_placeholder" android:textAlignment="center" android:textColor="@color/colorPrimaryDark" @@ -88,6 +86,7 @@ android:id="@+id/tvWalletAddressLabel" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginTop="8dp" android:text="@string/generate_address_label" android:textAlignment="center" android:textColor="@color/colorAccent" @@ -97,8 +96,6 @@ android:id="@+id/tvWalletAddress" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="8dp" - android:layout_marginTop="8dp" android:selectAllOnFocus="true" android:text="@string/generate_address_placeholder" android:textAlignment="center" @@ -110,6 +107,7 @@ android:id="@+id/tvWalletViewKeyLabel" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginTop="8dp" android:text="@string/generate_viewkey_label" android:textAlignment="center" android:textColor="@color/colorAccent" @@ -119,8 +117,6 @@ android:id="@+id/tvWalletViewKey" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="8dp" - android:layout_marginTop="8dp" android:selectAllOnFocus="true" android:text="@string/generate_viewkey_placeholder" android:textAlignment="center" @@ -132,6 +128,7 @@ android:id="@+id/tvWalletSpendKeyLabel" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginTop="8dp" android:text="@string/generate_spendkey_label" android:textAlignment="center" android:textColor="@color/colorAccent" @@ -141,8 +138,6 @@ android:id="@+id/tvWalletSpendKey" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="8dp" - android:layout_marginTop="8dp" android:selectAllOnFocus="true" android:text="@string/generate_spendkey_placeholder" android:textAlignment="center" @@ -154,8 +149,7 @@ android:id="@+id/bAccept" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="8dp" - android:layout_marginTop="8dp" + android:layout_marginTop="16dp" android:background="@color/colorPrimary" android:text="@string/generate_button_accept" /> From 032aa24ab51c37886e7d433f65bb8a627377faf0 Mon Sep 17 00:00:00 2001 From: m2049r <30435443+m2049r@users.noreply.github.com> Date: Wed, 16 Aug 2017 21:24:16 +0200 Subject: [PATCH 09/10] cleanup & AppCompatActivity & setTitle --- .../m2049r/xmrwallet/GenerateFragment.java | 21 +++++++++++-------- .../xmrwallet/GenerateReviewFragment.java | 3 --- .../com/m2049r/xmrwallet/LoginActivity.java | 8 ++++++- .../com/m2049r/xmrwallet/LoginFragment.java | 8 +++++++ .../com/m2049r/xmrwallet/WalletActivity.java | 3 ++- app/src/main/res/layout/gen_fragment.xml | 7 +------ .../main/res/layout/gen_review_fragment.xml | 7 +------ app/src/main/res/layout/login_activity.xml | 19 ++++++++++++++--- app/src/main/res/layout/login_fragment.xml | 7 +------ app/src/main/res/layout/wallet_activity.xml | 19 ++++++++++++++--- app/src/main/res/layout/wallet_fragment.xml | 12 +++-------- 11 files changed, 67 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java index 755b1ea2..5ef7b2b3 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java @@ -20,7 +20,6 @@ import android.app.Fragment; import android.content.Context; import android.os.Bundle; import android.text.Editable; -import android.text.InputType; import android.text.TextWatcher; import android.util.Log; import android.view.KeyEvent; @@ -178,8 +177,10 @@ public class GenerateFragment extends Fragment { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) { if (etWalletAddress.length() == 0) { - Helper.hideKeyboard(getActivity()); - generateWallet(); + if (bGenerate.getVisibility() == View.VISIBLE) { + Helper.hideKeyboard(getActivity()); + generateWallet(); + } } else if (addressOk()) { etWalletViewKey.requestFocus(); } else { @@ -247,8 +248,10 @@ public class GenerateFragment extends Fragment { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) { if (spendKeyOk()) { - Helper.hideKeyboard(getActivity()); - generateWallet(); + if (bGenerate.getVisibility() == View.VISIBLE) { + Helper.hideKeyboard(getActivity()); + generateWallet(); + } } else { Toast.makeText(getActivity(), getString(R.string.generate_check_key), Toast.LENGTH_LONG).show(); } @@ -267,8 +270,10 @@ public class GenerateFragment extends Fragment { etWalletRestoreHeight.setOnEditorActionListener(new TextView.OnEditorActionListener() { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) { - Helper.hideKeyboard(getActivity()); - generateWallet(); + if (bGenerate.getVisibility() == View.VISIBLE) { + Helper.hideKeyboard(getActivity()); + generateWallet(); + } return true; } return false; @@ -324,8 +329,6 @@ public class GenerateFragment extends Fragment { try { height = Long.parseLong(etWalletRestoreHeight.getText().toString()); } catch (NumberFormatException ex) { - Log.e(TAG, "Cannot parse " + etWalletRestoreHeight.getText().toString()); - Log.e(TAG, ex.getLocalizedMessage()); height = 0; // Keep calm and carry on! } diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java index 38be477a..71a145b7 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java @@ -27,8 +27,6 @@ import android.widget.TextView; import com.m2049r.xmrwallet.model.WalletManager; -import java.text.NumberFormat; - // TODO: somehow show which net we are generating for public class GenerateReviewFragment extends Fragment { @@ -93,7 +91,6 @@ public class GenerateReviewFragment extends Fragment { tvWalletMnemonic.setText(seed); tvWalletViewKey.setText(view); tvWalletSpendKey.setText(spend); - NumberFormat formatter = NumberFormat.getInstance(); bAccept.setEnabled(true); } diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java index 0d3d76dc..b9cba287 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java @@ -28,6 +28,7 @@ import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Bundle; import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -44,7 +45,7 @@ import com.m2049r.xmrwallet.util.Helper; import java.io.File; -public class LoginActivity extends Activity +public class LoginActivity extends AppCompatActivity implements LoginFragment.Listener, GenerateFragment.Listener, GenerateReviewFragment.Listener { static final String TAG = "LoginActivity"; @@ -166,6 +167,11 @@ public class LoginActivity extends Activity return Helper.getStorageRoot(getApplicationContext()); } + @Override + public void setTitle(String title) { + super.setTitle(title); + } + //////////////////////////////////////// //////////////////////////////////////// diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java index ae05a405..50e06272 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginFragment.java @@ -78,6 +78,8 @@ public class LoginFragment extends Fragment { File getStorageRoot(); void onWalletSelected(final String wallet); + + void setTitle(String title); } @Override @@ -135,6 +137,8 @@ public class LoginFragment extends Fragment { } else { setDaemon(daemonTestNet); } + activityCallback.setTitle(getString(R.string.app_name) + " " + + getString(mainnet ? R.string.connect_mainnet : R.string.connect_testnet)); filterList(); ((BaseAdapter) listView.getAdapter()).notifyDataSetChanged(); } @@ -185,6 +189,10 @@ public class LoginFragment extends Fragment { activityCallback.onWalletSelected(wallet); } }); + + activityCallback.setTitle(getString(R.string.app_name) + " " + + getString(isMainNet() ? R.string.connect_mainnet : R.string.connect_testnet)); + loadList(); return view; } diff --git a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java index c1c2bcb8..a0c5620b 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java @@ -25,13 +25,14 @@ import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.PowerManager; +import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.widget.Toast; import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.service.WalletService; -public class WalletActivity extends Activity implements WalletFragment.Listener, +public class WalletActivity extends AppCompatActivity implements WalletFragment.Listener, WalletService.Observer { private static final String TAG = "WalletActivity"; diff --git a/app/src/main/res/layout/gen_fragment.xml b/app/src/main/res/layout/gen_fragment.xml index 101e94e8..f9e23b82 100644 --- a/app/src/main/res/layout/gen_fragment.xml +++ b/app/src/main/res/layout/gen_fragment.xml @@ -2,12 +2,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:gravity="center_horizontal" - android:orientation="vertical" - android:paddingBottom="@dimen/activity_vertical_margin" - android:paddingLeft="@dimen/activity_horizontal_margin" - android:paddingRight="@dimen/activity_horizontal_margin" - android:paddingTop="@dimen/activity_vertical_margin"> + android:orientation="vertical"> <LinearLayout android:layout_width="fill_parent" diff --git a/app/src/main/res/layout/gen_review_fragment.xml b/app/src/main/res/layout/gen_review_fragment.xml index a41a8b7d..9332dc37 100644 --- a/app/src/main/res/layout/gen_review_fragment.xml +++ b/app/src/main/res/layout/gen_review_fragment.xml @@ -2,12 +2,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:gravity="center_horizontal" - android:orientation="vertical" - android:paddingBottom="@dimen/activity_vertical_margin" - android:paddingLeft="@dimen/activity_horizontal_margin" - android:paddingRight="@dimen/activity_horizontal_margin" - android:paddingTop="@dimen/activity_vertical_margin"> + android:orientation="vertical"> <LinearLayout android:layout_width="fill_parent" diff --git a/app/src/main/res/layout/login_activity.xml b/app/src/main/res/layout/login_activity.xml index 017e4013..6c2ccbef 100644 --- a/app/src/main/res/layout/login_activity.xml +++ b/app/src/main/res/layout/login_activity.xml @@ -1,4 +1,17 @@ -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/fragment_container" +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="match_parent" /> \ No newline at end of file + android:layout_height="match_parent" + android:gravity="center_horizontal" + android:orientation="vertical" + android:paddingBottom="@dimen/activity_vertical_margin" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin"> + + <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/fragment_container" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + +</LinearLayout> diff --git a/app/src/main/res/layout/login_fragment.xml b/app/src/main/res/layout/login_fragment.xml index 8ae9995d..bf234203 100644 --- a/app/src/main/res/layout/login_fragment.xml +++ b/app/src/main/res/layout/login_fragment.xml @@ -2,12 +2,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:gravity="center_horizontal" - android:orientation="vertical" - android:paddingBottom="@dimen/activity_vertical_margin" - android:paddingLeft="@dimen/activity_horizontal_margin" - android:paddingRight="@dimen/activity_horizontal_margin" - android:paddingTop="@dimen/activity_vertical_margin"> + android:orientation="vertical"> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" diff --git a/app/src/main/res/layout/wallet_activity.xml b/app/src/main/res/layout/wallet_activity.xml index 017e4013..6c2ccbef 100644 --- a/app/src/main/res/layout/wallet_activity.xml +++ b/app/src/main/res/layout/wallet_activity.xml @@ -1,4 +1,17 @@ -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/fragment_container" +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="match_parent" /> \ No newline at end of file + android:layout_height="match_parent" + android:gravity="center_horizontal" + android:orientation="vertical" + android:paddingBottom="@dimen/activity_vertical_margin" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin"> + + <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/fragment_container" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + +</LinearLayout> diff --git a/app/src/main/res/layout/wallet_fragment.xml b/app/src/main/res/layout/wallet_fragment.xml index 7c80acd9..2a2e5789 100644 --- a/app/src/main/res/layout/wallet_fragment.xml +++ b/app/src/main/res/layout/wallet_fragment.xml @@ -1,16 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@android:color/white" - android:orientation="vertical" - android:paddingBottom="@dimen/activity_vertical_margin" - android:paddingLeft="@dimen/activity_horizontal_margin" - android:paddingRight="@dimen/activity_horizontal_margin" - android:paddingTop="@dimen/activity_vertical_margin" - > + android:orientation="vertical"> - <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" +<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" From 9f38b957e180e4ddbd8c15fe91ea111bb0b1d841 Mon Sep 17 00:00:00 2001 From: m2049r <30435443+m2049r@users.noreply.github.com> Date: Thu, 17 Aug 2017 10:57:24 +0200 Subject: [PATCH 10/10] lots of minor fixes & tweaks. do not copy cache. the cache file is corrupt after recovery (except watch only). --- app/src/main/cpp/monerujo.cpp | 2 +- .../m2049r/xmrwallet/GenerateFragment.java | 146 ++++++++++-------- .../xmrwallet/GenerateReviewFragment.java | 10 +- .../com/m2049r/xmrwallet/LoginActivity.java | 84 ++++++---- .../xmrwallet/service/WalletService.java | 5 +- app/src/main/res/values/strings.xml | 2 + 6 files changed, 149 insertions(+), 100 deletions(-) diff --git a/app/src/main/cpp/monerujo.cpp b/app/src/main/cpp/monerujo.cpp index 79be0389..1d14de8a 100644 --- a/app/src/main/cpp/monerujo.cpp +++ b/app/src/main/cpp/monerujo.cpp @@ -302,7 +302,7 @@ Java_com_m2049r_xmrwallet_model_WalletManager_recoveryWalletJ(JNIEnv *env, jobje return reinterpret_cast<jlong>(wallet); } -JNIEXPORT jboolean JNICALL +JNIEXPORT jlong JNICALL Java_com_m2049r_xmrwallet_model_WalletManager_createWalletFromKeysJ(JNIEnv *env, jobject instance, jstring path, jstring language, jboolean isTestNet, diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java index 5ef7b2b3..d1f846ad 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java @@ -21,7 +21,6 @@ import android.content.Context; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; -import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; @@ -38,8 +37,6 @@ import com.m2049r.xmrwallet.util.Helper; import java.io.File; -// TODO: somehow show which net we are generating for - public class GenerateFragment extends Fragment { static final String TAG = "GenerateFragment"; @@ -119,7 +116,11 @@ public class GenerateFragment extends Fragment { etWalletPassword.setOnEditorActionListener(new TextView.OnEditorActionListener() { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) { - etWalletMnemonic.requestFocus(); + if (etWalletAddress.length() > 0) { + etWalletAddress.requestFocus(); + } else { + etWalletMnemonic.requestFocus(); + } return true; } return false; @@ -141,39 +142,45 @@ public class GenerateFragment extends Fragment { return false; } }); - etWalletMnemonic.setOnClickListener(new View.OnClickListener() { + etWalletMnemonic.setOnClickListener(new View.OnClickListener() + + { @Override public void onClick(View v) { Helper.showKeyboard(getActivity()); } }); - etWalletMnemonic.addTextChangedListener(new TextWatcher() { - @Override - public void afterTextChanged(Editable editable) { - if (etWalletMnemonic.length() > 0) { - etWalletRestoreHeight.setVisibility(View.VISIBLE); - etWalletAddress.setVisibility(View.INVISIBLE); - } else { - etWalletAddress.setVisibility(View.VISIBLE); - if (etWalletAddress.length() == 0) { - etWalletRestoreHeight.setVisibility(View.INVISIBLE); - } else { - etWalletRestoreHeight.setVisibility(View.VISIBLE); - } + etWalletMnemonic.addTextChangedListener(new - } - } + TextWatcher() { + @Override + public void afterTextChanged(Editable editable) { + if (etWalletMnemonic.length() > 0) { + etWalletRestoreHeight.setVisibility(View.VISIBLE); + etWalletAddress.setVisibility(View.INVISIBLE); + } else { + etWalletAddress.setVisibility(View.VISIBLE); + if (etWalletAddress.length() == 0) { + etWalletRestoreHeight.setVisibility(View.INVISIBLE); + } else { + etWalletRestoreHeight.setVisibility(View.VISIBLE); + } - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } + } + } - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - }); + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } - etWalletAddress.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + }); + + etWalletAddress.setOnEditorActionListener(new TextView.OnEditorActionListener() + + { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) { if (etWalletAddress.length() == 0) { @@ -191,40 +198,46 @@ public class GenerateFragment extends Fragment { return false; } }); - etWalletAddress.setOnClickListener(new View.OnClickListener() { + etWalletAddress.setOnClickListener(new View.OnClickListener() + + { @Override public void onClick(View v) { Helper.showKeyboard(getActivity()); } }); - etWalletAddress.addTextChangedListener(new TextWatcher() { - @Override - public void afterTextChanged(Editable editable) { - if (etWalletAddress.length() > 0) { - llRestoreKeys.setVisibility(View.VISIBLE); - etWalletMnemonic.setVisibility(View.INVISIBLE); - etWalletRestoreHeight.setVisibility(View.VISIBLE); - } else { - llRestoreKeys.setVisibility(View.INVISIBLE); - etWalletMnemonic.setVisibility(View.VISIBLE); - if (etWalletMnemonic.length() == 0) { - etWalletRestoreHeight.setVisibility(View.INVISIBLE); - } else { - etWalletRestoreHeight.setVisibility(View.VISIBLE); - } - } - } + etWalletAddress.addTextChangedListener(new - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } + TextWatcher() { + @Override + public void afterTextChanged(Editable editable) { + if (etWalletAddress.length() > 0) { + llRestoreKeys.setVisibility(View.VISIBLE); + etWalletMnemonic.setVisibility(View.INVISIBLE); + etWalletRestoreHeight.setVisibility(View.VISIBLE); + } else { + llRestoreKeys.setVisibility(View.INVISIBLE); + etWalletMnemonic.setVisibility(View.VISIBLE); + if (etWalletMnemonic.length() == 0) { + etWalletRestoreHeight.setVisibility(View.INVISIBLE); + } else { + etWalletRestoreHeight.setVisibility(View.VISIBLE); + } + } + } - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - }); + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } - etWalletViewKey.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + }); + + etWalletViewKey.setOnEditorActionListener(new TextView.OnEditorActionListener() + + { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) { if (viewKeyOk()) { @@ -237,21 +250,22 @@ public class GenerateFragment extends Fragment { return false; } }); - etWalletViewKey.setOnClickListener(new View.OnClickListener() { + etWalletViewKey.setOnClickListener(new View.OnClickListener() + + { @Override public void onClick(View v) { Helper.showKeyboard(getActivity()); } }); - etWalletSpendKey.setOnEditorActionListener(new TextView.OnEditorActionListener() { + etWalletSpendKey.setOnEditorActionListener(new TextView.OnEditorActionListener() + + { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) { if (spendKeyOk()) { - if (bGenerate.getVisibility() == View.VISIBLE) { - Helper.hideKeyboard(getActivity()); - generateWallet(); - } + etWalletRestoreHeight.requestFocus(); } else { Toast.makeText(getActivity(), getString(R.string.generate_check_key), Toast.LENGTH_LONG).show(); } @@ -260,14 +274,18 @@ public class GenerateFragment extends Fragment { return false; } }); - etWalletSpendKey.setOnClickListener(new View.OnClickListener() { + etWalletSpendKey.setOnClickListener(new View.OnClickListener() + + { @Override public void onClick(View v) { Helper.showKeyboard(getActivity()); } }); - etWalletRestoreHeight.setOnEditorActionListener(new TextView.OnEditorActionListener() { + etWalletRestoreHeight.setOnEditorActionListener(new TextView.OnEditorActionListener() + + { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) { if (bGenerate.getVisibility() == View.VISIBLE) { @@ -280,7 +298,9 @@ public class GenerateFragment extends Fragment { } }); - bGenerate.setOnClickListener(new View.OnClickListener() { + bGenerate.setOnClickListener(new View.OnClickListener() + + { @Override public void onClick(View v) { Helper.hideKeyboard(getActivity()); diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java index 71a145b7..8089323d 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java @@ -27,8 +27,6 @@ import android.widget.TextView; import com.m2049r.xmrwallet.model.WalletManager; -// TODO: somehow show which net we are generating for - public class GenerateReviewFragment extends Fragment { static final String TAG = "GenerateReviewFragment"; @@ -84,13 +82,17 @@ public class GenerateReviewFragment extends Fragment { String seed = b.getString("seed"); String view = b.getString("viewkey"); String spend = b.getString("spendkey"); - long height = b.getLong("restoreHeight"); + tvWalletName.setText(name); tvWalletPassword.setText(password); tvWalletAddress.setText(address); tvWalletMnemonic.setText(seed); tvWalletViewKey.setText(view); - tvWalletSpendKey.setText(spend); + if (spend.length() > 0) { // should be == 64, but spendkey is not in the API yet + tvWalletSpendKey.setText(spend); + } else { + tvWalletSpendKey.setText(getString(R.string.generate_wallet_watchonly)); + } bAccept.setEnabled(true); } diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java index b9cba287..1ddc8df8 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java @@ -27,6 +27,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Bundle; +import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.v7.app.AppCompatActivity; import android.util.Log; @@ -44,6 +45,10 @@ import com.m2049r.xmrwallet.service.MoneroHandlerThread; import com.m2049r.xmrwallet.util.Helper; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; public class LoginActivity extends AppCompatActivity implements LoginFragment.Listener, GenerateFragment.Listener, GenerateReviewFragment.Listener { @@ -288,8 +293,7 @@ public class LoginActivity extends AppCompatActivity final String seed = newWallet.getSeed(); final String address = newWallet.getAddress(); final String view = newWallet.getSecretViewKey(); - final long height = newWallet.getBlockChainHeight(); - final String spend = "not available - use seed for recovery"; //TODO + final String spend = newWallet.isWatchOnly() ? "" : "not available - use seed for recovery"; newWallet.close(); Log.d(TAG, "Created " + address); runOnUiThread(new Runnable() { @@ -301,7 +305,6 @@ public class LoginActivity extends AppCompatActivity b.putString("address", address); b.putString("viewkey", view); b.putString("spendkey", spend); - b.putLong("restoreHeight", height); startReviewFragment(b); } }); @@ -321,7 +324,6 @@ public class LoginActivity extends AppCompatActivity public Wallet createWallet(String path, String password) { return WalletManager.getInstance() .createWallet(path, password, MNEMONIC_LANGUAGE); - } }); } @@ -358,37 +360,16 @@ public class LoginActivity extends AppCompatActivity @Override public void onAccept(final String name, final String password) { - File newWalletFolder = new File(getStorageRoot(), ".new"); - if (!newWalletFolder.isDirectory()) { - Log.e(TAG, "New wallet dir " + newWalletFolder.getAbsolutePath() + "is not a directory"); - return; - } - final String newWalletPath = new File(newWalletFolder, name).getAbsolutePath(); + final File newWalletFolder = new File(getStorageRoot(), ".new"); + final File walletFolder = getStorageRoot(); new Thread(null, new Runnable() { @Override public void run() { - Log.d(TAG, "opening wallet " + newWalletPath); - Wallet newWallet = WalletManager.getInstance() - .openWallet(newWalletPath, password); - Wallet.Status status = newWallet.getStatus(); - Log.d(TAG, "wallet opened " + newWallet.getStatus()); - if (status != Wallet.Status.Status_Ok) { - Log.e(TAG, "New wallet is " + status.toString()); - runOnUiThread(new Runnable() { - public void run() { - Toast.makeText(LoginActivity.this, - getString(R.string.generate_wallet_create_failed_1), Toast.LENGTH_LONG).show(); - } - }); - newWallet.close(); - return; - } - final String walletPath = new File(getStorageRoot(), name).getAbsolutePath(); - final boolean rc = newWallet.store(walletPath); - Log.d(TAG, "wallet stored with rc=" + rc); - newWallet.close(); - Log.d(TAG, "wallet closed"); + final String walletPath = new File(walletFolder, name).getAbsolutePath(); + final boolean rc = copyWallet(walletFolder, newWalletFolder, name) + && + (testWallet(walletPath, password) == Wallet.Status.Status_Ok); runOnUiThread(new Runnable() { public void run() { if (rc) { @@ -408,4 +389,45 @@ public class LoginActivity extends AppCompatActivity , "AcceptWallet", MoneroHandlerThread.THREAD_STACK_SIZE).start(); } + Wallet.Status testWallet(String path, String password) { + Log.d(TAG, "testing wallet " + path); + Wallet aWallet = WalletManager.getInstance().openWallet(path, password); + if (aWallet == null) return Wallet.Status.Status_Error; // does this ever happen? + Wallet.Status status = aWallet.getStatus(); + Log.d(TAG, "wallet tested " + aWallet.getStatus()); + aWallet.close(); + return status; + } + + boolean copyWallet(File dstDir, File srcDir, String name) { + boolean success = false; + try { + // TODO: the cache is corrupt if we recover (!!) + // TODO recoveryheight is ignored but not on watchonly wallet ?! - find out why + //copyFile(dstDir, srcDir, name); + copyFile(dstDir, srcDir, name + ".keys"); + copyFile(dstDir, srcDir, name + ".address.txt"); + success = true; + } catch (IOException ex) { + Log.e(TAG, "wallet copy failed: " + ex.getMessage()); + // try to rollback + new File(dstDir, name).delete(); + new File(dstDir, name + ".keys").delete(); + new File(dstDir, name + ".address.txt").delete(); + } + return success; + } + + void copyFile(File dstDir, File srcDir, String name) throws IOException { + FileChannel inChannel = new FileInputStream(new File(srcDir, name)).getChannel(); + FileChannel outChannel = new FileOutputStream(new File(dstDir, name)).getChannel(); + try { + inChannel.transferTo(0, inChannel.size(), outChannel); + } finally { + if (inChannel != null) + inChannel.close(); + if (outChannel != null) + outChannel.close(); + } + } } diff --git a/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java b/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java index f67ab9b8..ce4bc3c9 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java +++ b/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java @@ -111,7 +111,8 @@ public class WalletService extends Service { fullRefresh = true; } } - observer.onRefreshed(wallet, fullRefresh); + if (observer != null) + observer.onRefreshed(wallet, fullRefresh); } } } @@ -354,6 +355,7 @@ public class WalletService extends Service { if (listener == null) { Log.d(TAG, "start() loadWallet"); Wallet aWallet = loadWallet(walletName, walletPassword); + // TODO check aWallet and die gracefully if neccessary listener = new MyWalletListener(aWallet); listener.start(); showProgress(100); @@ -420,6 +422,7 @@ public class WalletService extends Service { wallet = null; // TODO what do we do with the progress?? // TODO tell the activity this failed + // this crashes in MyWalletListener(Wallet aWallet) as wallet == null } } return wallet; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6f27b22c..9ec235a0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -53,6 +53,8 @@ <string name="generate_button_accept">I have noted the mnemonic seed\nNow, I want to loose all my money!</string> <string name="generate_button_reset">I\'m confused - Let me start again!</string> + <string name="generate_wallet_watchonly"><Watch Only Wallet></string> + <string name="generate_wallet_exists">Wallet exists! Choose another name</string> <string name="generate_wallet_created">Wallet created</string> <string name="generate_wallet_create_failed_1">Wallet create failed (1/2)</string>