mirror of https://github.com/m2049r/xmrwallet.git
cleanup + docs
This commit is contained in:
parent
37414be4bf
commit
30e35a895d
|
@ -1 +1,2 @@
|
|||
workspace.xml
|
||||
markdown-navigator*
|
||||
|
|
28
README.md
28
README.md
|
@ -3,39 +3,35 @@ Another Android Monero Wallet
|
|||
|
||||
### QUICKSTART
|
||||
- Download APK (Release) and install it
|
||||
- Copy over synced wallet (all three files) onto sdcard in directory Monerujo (created first time app is started)
|
||||
- Start app (again)
|
||||
- Run the App and select "Generate Wallet" to create a new wallet or recover a wallet
|
||||
- Advanced users could copy over synced wallet files (all files) onto sdcard in directory Monerujo (created first time App is started)
|
||||
- see the [FAQ](doc/FAQ.md)
|
||||
|
||||
### Disclaimer
|
||||
This is my first serious Android App.
|
||||
You may loose all your Moneroj if you use this App.
|
||||
|
||||
### Random Notes
|
||||
- Based off monero v0.10.3.1 with pull requests #2238, #2239 and #2289 applied => so can be used in mainnet!
|
||||
- currently only android32
|
||||
- currently only use is checking incoming/outgoing transactions
|
||||
- works in testnet & mainnet (please use your own daemons)
|
||||
- takes forever to sync on mainnet (even with own daemon) - due to 32-bit architecture
|
||||
- ~~currently only use is checking incoming/outgoing transactions~~
|
||||
- works in testnet & mainnet
|
||||
- takes forever to sync due to 32-bit architecture
|
||||
- use your own daemon - it's easy
|
||||
- screen stays on until first sync is complete
|
||||
- saves wallet only on first sync
|
||||
- saves wallet only on first sync and when sending transactions or editing notes
|
||||
- Monerujo means "Monero Wallet" according to https://www.reddit.com/r/Monero/comments/3exy7t/esperanto_corner/
|
||||
|
||||
### TODO
|
||||
- make it pretty
|
||||
- wallet backup functions
|
||||
- adjust layout so we can use bigger font sizes (maybe show only 5 decimal places instead of 12 in main view)
|
||||
- review visibility of methods/classes
|
||||
- sensible error dialogs (e.g. when no write permissions granted) instead of just crashing on purpose
|
||||
- spend monero - not so difficult with wallet api
|
||||
- more sensible error dialogs ~~(e.g. when no write permissions granted) instead of just crashing on purpose~~
|
||||
- check licenses of included libraries; License Dialog
|
||||
- ~~provide detailed build instructions for third party binaries~~
|
||||
- ~~sensible loading/saving progress bars instead of just freezing up~~
|
||||
- ~~figure out how to make it all flow better (loading/saving takes forever and does not run in background)~~
|
||||
- ~~currently loading in background thread produces segfaults in JNI~~
|
||||
- ~~make it pretty~~ (decided to go with "form follows function")
|
||||
- ~~spend monero - not so difficult with wallet api~~
|
||||
|
||||
### Issues
|
||||
- ~~screen rotation crashes the app~~
|
||||
- ~~turning the display off/on during sync stops sync~~
|
||||
none :)
|
||||
|
||||
### HOW TO BUILD
|
||||
No need to build. Binaries are included:
|
||||
|
|
|
@ -19,14 +19,13 @@
|
|||
android:name=".WalletActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:label="@string/wallet_activity_name"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"></activity>
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".LoginActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:label="@string/app_name"
|
||||
android:screenOrientation="portrait"
|
||||
android:configChanges="orientation|keyboardHidden">
|
||||
android:screenOrientation="portrait">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
|
|
@ -142,11 +142,11 @@ public class GenerateFragment extends Fragment {
|
|||
public void afterTextChanged(Editable editable) {
|
||||
if (etWalletMnemonic.length() > 0) {
|
||||
etWalletRestoreHeight.setVisibility(View.VISIBLE);
|
||||
etWalletAddress.setVisibility(View.INVISIBLE);
|
||||
etWalletAddress.setVisibility(View.GONE);
|
||||
} else {
|
||||
etWalletAddress.setVisibility(View.VISIBLE);
|
||||
if (etWalletAddress.length() == 0) {
|
||||
etWalletRestoreHeight.setVisibility(View.INVISIBLE);
|
||||
etWalletRestoreHeight.setVisibility(View.GONE);
|
||||
} else {
|
||||
etWalletRestoreHeight.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
@ -193,10 +193,10 @@ public class GenerateFragment extends Fragment {
|
|||
etWalletMnemonic.setVisibility(View.INVISIBLE);
|
||||
etWalletRestoreHeight.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
llRestoreKeys.setVisibility(View.INVISIBLE);
|
||||
llRestoreKeys.setVisibility(View.GONE);
|
||||
etWalletMnemonic.setVisibility(View.VISIBLE);
|
||||
if (etWalletMnemonic.length() == 0) {
|
||||
etWalletRestoreHeight.setVisibility(View.INVISIBLE);
|
||||
etWalletRestoreHeight.setVisibility(View.GONE);
|
||||
} else {
|
||||
etWalletRestoreHeight.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
@ -280,8 +280,8 @@ public class GenerateFragment extends Fragment {
|
|||
|
||||
private boolean addressOk() {
|
||||
String address = etWalletAddress.getText().toString();
|
||||
// TODO only accept address from the correct net
|
||||
return ((address.length() == 95) && ("49A".indexOf(address.charAt(0)) >= 0));
|
||||
boolean testnet = WalletManager.getInstance().isTestNet();
|
||||
return ((address.length() == 95) && ((testnet ? "9A" : "4").indexOf(address.charAt(0)) >= 0));
|
||||
}
|
||||
|
||||
private boolean viewKeyOk() {
|
||||
|
@ -318,19 +318,19 @@ public class GenerateFragment extends Fragment {
|
|||
// figure out how we want to create this wallet
|
||||
// A. from scratch
|
||||
if ((seed.length() == 0) && (address.length() == 0)) {
|
||||
bGenerate.setVisibility(View.INVISIBLE);
|
||||
bGenerate.setVisibility(View.GONE);
|
||||
activityCallback.onGenerate(name, password);
|
||||
} else
|
||||
// B. from seed
|
||||
if (mnemonicOk()) {
|
||||
bGenerate.setVisibility(View.INVISIBLE);
|
||||
bGenerate.setVisibility(View.GONE);
|
||||
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);
|
||||
bGenerate.setVisibility(View.GONE);
|
||||
activityCallback.onGenerate(name, password, address, viewKey, spendKey, height);
|
||||
} else
|
||||
// D. none of the above :)
|
||||
|
|
|
@ -36,6 +36,7 @@ public class GenerateReviewFragment extends Fragment {
|
|||
static final String TAG = "GenerateReviewFragment";
|
||||
static final public String VIEW_DETAILS = "details";
|
||||
static final public String VIEW_ACCEPT = "accept";
|
||||
static final public String VIEW_WALLET = "wallet";
|
||||
|
||||
ProgressBar pbProgress;
|
||||
TextView tvWalletName;
|
||||
|
@ -75,12 +76,15 @@ public class GenerateReviewFragment extends Fragment {
|
|||
showProgress();
|
||||
|
||||
Bundle b = getArguments();
|
||||
String type = b.getString("type");
|
||||
if (!type.equals(VIEW_WALLET)) {
|
||||
String name = b.getString("name");
|
||||
String password = b.getString("password");
|
||||
String type = b.getString("type");
|
||||
tvWalletName.setText(new File(name).getName());
|
||||
show(name, password, type);
|
||||
|
||||
} else {
|
||||
show(walletCallback.getWallet(), null, type);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
|
@ -88,7 +92,7 @@ public class GenerateReviewFragment extends Fragment {
|
|||
String name = tvWalletName.getText().toString();
|
||||
String password = tvWalletPassword.getText().toString();
|
||||
bAccept.setEnabled(false);
|
||||
activityCallback.onAccept(name, password);
|
||||
acceptCallback.onAccept(name, password);
|
||||
}
|
||||
|
||||
private void show(final String walletPath, final String password, final String type) {
|
||||
|
@ -96,25 +100,29 @@ public class GenerateReviewFragment extends Fragment {
|
|||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Wallet wallet = WalletManager.getInstance().openWallet(walletPath, password);
|
||||
final String name = wallet.getName();
|
||||
final String seed = wallet.getSeed();
|
||||
final String address = wallet.getAddress();
|
||||
final String view = wallet.getSecretViewKey();
|
||||
final String spend = wallet.isWatchOnly() ? "" : "not available - use seed for recovery";
|
||||
wallet.close();
|
||||
|
||||
final Wallet wallet = WalletManager.getInstance().openWallet(walletPath, password);
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
show(wallet, password, type);
|
||||
}
|
||||
});
|
||||
wallet.close();
|
||||
}
|
||||
}
|
||||
, "DetailsReview", MoneroHandlerThread.THREAD_STACK_SIZE).start();
|
||||
}
|
||||
|
||||
private void show(final Wallet wallet, final String password, final String type) {
|
||||
if (type.equals(GenerateReviewFragment.VIEW_ACCEPT)) {
|
||||
tvWalletPassword.setText(password);
|
||||
bAccept.setVisibility(View.VISIBLE);
|
||||
bAccept.setEnabled(true);
|
||||
}
|
||||
tvWalletName.setText(name);
|
||||
tvWalletAddress.setText(address);
|
||||
tvWalletMnemonic.setText(seed);
|
||||
tvWalletViewKey.setText(view);
|
||||
tvWalletName.setText(wallet.getName());
|
||||
tvWalletAddress.setText(wallet.getAddress());
|
||||
tvWalletMnemonic.setText(wallet.getSeed());
|
||||
tvWalletViewKey.setText(wallet.getSecretViewKey());
|
||||
String spend = wallet.isWatchOnly() ? "" : "not available - use seed for recovery";
|
||||
if (spend.length() > 0) { //TODO should be == 64, but spendkey is not in the API yet
|
||||
tvWalletSpendKey.setText(spend);
|
||||
} else {
|
||||
|
@ -122,24 +130,25 @@ public class GenerateReviewFragment extends Fragment {
|
|||
}
|
||||
hideProgress();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
, "DetailsReview", MoneroHandlerThread.THREAD_STACK_SIZE).start();
|
||||
|
||||
}
|
||||
|
||||
GenerateReviewFragment.Listener activityCallback;
|
||||
GenerateReviewFragment.Listener acceptCallback = null;
|
||||
GenerateReviewFragment.ListenerWithWallet walletCallback = null;
|
||||
|
||||
public interface Listener {
|
||||
void onAccept(String name, String password);
|
||||
}
|
||||
|
||||
public interface ListenerWithWallet {
|
||||
Wallet getWallet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof GenerateReviewFragment.Listener) {
|
||||
this.activityCallback = (GenerateReviewFragment.Listener) context;
|
||||
this.acceptCallback = (GenerateReviewFragment.Listener) context;
|
||||
} else if (context instanceof GenerateReviewFragment.ListenerWithWallet) {
|
||||
this.walletCallback = (GenerateReviewFragment.ListenerWithWallet) context;
|
||||
} else {
|
||||
throw new ClassCastException(context.toString()
|
||||
+ " must implement Listener");
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package com.m2049r.xmrwallet;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
|
@ -29,6 +28,7 @@ import android.content.pm.PackageManager;
|
|||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -40,7 +40,6 @@ 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;
|
||||
|
@ -63,6 +62,11 @@ public class LoginActivity extends AppCompatActivity
|
|||
if (savedInstanceState != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.tbLogin);
|
||||
toolbar.setTitle(R.string.login_activity_name);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
if (Helper.getWritePermission(this)) {
|
||||
startLoginFragment();
|
||||
} else {
|
||||
|
@ -235,7 +239,6 @@ public class LoginActivity extends AppCompatActivity
|
|||
startReviewFragment(b);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
|
||||
Log.d(TAG, "onRequestPermissionsResult()");
|
||||
|
@ -420,9 +423,10 @@ public class LoginActivity extends AppCompatActivity
|
|||
boolean copyWallet(File dstDir, File srcDir, String name) {
|
||||
boolean success = false;
|
||||
try {
|
||||
// TODO: the cache is corrupt if we recover (!!)
|
||||
// TODO: the cache is ok if we immediately to a full refresh()
|
||||
// TODO recoveryheight is ignored but not on watchonly wallet ?! - find out why
|
||||
// the cache is corrupt if we recover (!!)
|
||||
// the cache is ok if we immediately do a full refresh()
|
||||
// recoveryheight is ignored but not on watchonly wallet ?! - find out why
|
||||
// so we just ignore the cache file and rebuild it on first sync
|
||||
//copyFile(dstDir, srcDir, name);
|
||||
copyFile(dstDir, srcDir, name + ".keys");
|
||||
copyFile(dstDir, srcDir, name + ".address.txt");
|
||||
|
|
|
@ -28,17 +28,21 @@ import android.os.Bundle;
|
|||
import android.os.IBinder;
|
||||
import android.os.PowerManager;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.m2049r.xmrwallet.model.PendingTransaction;
|
||||
import com.m2049r.xmrwallet.model.TransactionInfo;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.model.WalletManager;
|
||||
import com.m2049r.xmrwallet.service.WalletService;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.util.TxData;
|
||||
|
||||
public class WalletActivity extends AppCompatActivity implements WalletFragment.Listener,
|
||||
WalletService.Observer, SendFragment.Listener, TxFragment.Listener {
|
||||
WalletService.Observer, SendFragment.Listener, TxFragment.Listener, GenerateReviewFragment.ListenerWithWallet {
|
||||
private static final String TAG = "WalletActivity";
|
||||
|
||||
static final int MIN_DAEMON_VERSION = 65544;
|
||||
|
@ -46,6 +50,8 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
|
|||
public static final String REQUEST_ID = "id";
|
||||
public static final String REQUEST_PW = "pw";
|
||||
|
||||
Toolbar tbWallet;
|
||||
|
||||
private boolean synced = false;
|
||||
|
||||
@Override
|
||||
|
@ -109,21 +115,29 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
|
|||
Log.d(TAG, "onCreate()");
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.wallet_activity);
|
||||
if (savedInstanceState != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
tbWallet = (Toolbar) findViewById(R.id.tbWallet);
|
||||
tbWallet.setTitle(R.string.app_name);
|
||||
setSupportActionBar(tbWallet);
|
||||
tbWallet.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onWalletDetails();
|
||||
}
|
||||
});
|
||||
|
||||
Fragment walletFragment = new WalletFragment();
|
||||
getFragmentManager().beginTransaction()
|
||||
.add(R.id.fragment_container, walletFragment).commit();
|
||||
Log.d(TAG, "fragment added");
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
startWalletService();
|
||||
Log.d(TAG, "onCreate() done.");
|
||||
}
|
||||
|
||||
|
||||
public Wallet getWallet() {
|
||||
if (mBoundService == null) throw new IllegalStateException("WalletService not bound.");
|
||||
return mBoundService.getWallet();
|
||||
|
@ -256,7 +270,7 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
|
|||
|
||||
@Override
|
||||
public void setTitle(String title) {
|
||||
super.setTitle(title);
|
||||
tbWallet.setTitle(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -522,4 +536,13 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
|
|||
transaction.addToBackStack(stackName);
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
private void onWalletDetails() {
|
||||
Fragment fragment = getFragmentManager().findFragmentById(R.id.fragment_container);
|
||||
if (fragment instanceof WalletFragment) {
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString("type", GenerateReviewFragment.VIEW_WALLET);
|
||||
replaceFragment(new GenerateReviewFragment(), null, extras);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -116,7 +116,7 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O
|
|||
updateStatus(wallet);
|
||||
}
|
||||
|
||||
public void onSynced() { // TODO watchonly
|
||||
public void onSynced() {
|
||||
if (!activityCallback.isWatchOnly()) {
|
||||
bSend.setVisibility(View.VISIBLE);
|
||||
bSend.setEnabled(true);
|
||||
|
|
|
@ -81,6 +81,7 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
|||
|
||||
public void setInfos(List<TransactionInfo> data) {
|
||||
// TODO do stuff with data so we can really recycle elements (i.e. add only new tx)
|
||||
// as the TransactionInfo items are always recreated, we cannot recycle
|
||||
this.infoItems.clear();
|
||||
if (data != null) {
|
||||
Log.d(TAG, "setInfos " + data.size());
|
||||
|
@ -135,9 +136,8 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
|||
void bind(int position) {
|
||||
this.infoItem = infoItems.get(position);
|
||||
String displayAmount = Wallet.getDisplayAmount(infoItem.amount);
|
||||
// TODO fix this with i8n code
|
||||
// TODO fix this with i8n code but cryptonote::print_money always uses '.' for decimal point
|
||||
String amountParts[] = displayAmount.split("\\.");
|
||||
// TODO what if there is no decimal point?
|
||||
|
||||
this.tvAmount.setText(amountParts[0]);
|
||||
this.tvAmountDecimal.setText(amountParts[1]);
|
||||
|
|
|
@ -38,7 +38,7 @@ public class WalletManager {
|
|||
// no need to keep a reference to the REAL WalletManager (we get it every tvTime we need it)
|
||||
private static WalletManager Instance = null;
|
||||
|
||||
public static WalletManager getInstance() { // TODO not threadsafe
|
||||
public static synchronized WalletManager getInstance() {
|
||||
if (WalletManager.Instance == null) {
|
||||
WalletManager.Instance = new WalletManager();
|
||||
}
|
||||
|
|
|
@ -120,12 +120,11 @@ public class WalletService extends Service {
|
|||
updateDaemonState(wallet, wallet.isSynchronized() ? height : 0);
|
||||
if (!wallet.isSynchronized()) {
|
||||
// we want to see our transactions as they come in
|
||||
Log.d(TAG, "newBlock() refresh history");
|
||||
wallet.getHistory().refresh();
|
||||
Log.d(TAG, "newBlock() history refreshed");
|
||||
int txCount = wallet.getHistory().getCount();
|
||||
if (txCount > lastTxCount) {
|
||||
lastTxCount = txCount; // TODO maybe do this later
|
||||
// update the transaction list only if we have more than before
|
||||
lastTxCount = txCount;
|
||||
fullRefresh = true;
|
||||
}
|
||||
}
|
||||
|
@ -173,10 +172,9 @@ public class WalletService extends Service {
|
|||
// these calls really connect to the daemon - wasting time
|
||||
daemonHeight = wallet.getDaemonBlockChainHeight();
|
||||
if (daemonHeight > 0) {
|
||||
// if we get a valid height, the obviously we are connected
|
||||
// if we get a valid height, then obviously we are connected
|
||||
connectionStatus = Wallet.ConnectionStatus.ConnectionStatus_Connected;
|
||||
} else {
|
||||
// TODO: or connectionStatus = wallet.getConnectionStatus(); ?
|
||||
connectionStatus = Wallet.ConnectionStatus.ConnectionStatus_Disconnected;
|
||||
}
|
||||
}
|
||||
|
@ -485,7 +483,6 @@ public class WalletService extends Service {
|
|||
listener = null;
|
||||
}
|
||||
stopSelf();
|
||||
// TODO ensure the Looper & thread actually stop and go away?
|
||||
}
|
||||
|
||||
private Wallet loadWallet(String walletName, String walletPassword) {
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="invisible">
|
||||
android:visibility="gone">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etWalletViewKey"
|
||||
|
@ -92,7 +92,7 @@
|
|||
android:inputType="number"
|
||||
android:textAlignment="center"
|
||||
android:textSize="16sp"
|
||||
android:visibility="invisible" />
|
||||
android:visibility="gone" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/bGenerate"
|
||||
|
|
|
@ -3,11 +3,14 @@
|
|||
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.v7.widget.Toolbar
|
||||
android:id="@+id/tbLogin"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:elevation="4dp" />
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/fragment_container"
|
||||
|
|
|
@ -3,11 +3,14 @@
|
|||
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.v7.widget.Toolbar
|
||||
android:id="@+id/tbWallet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:elevation="4dp" />
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/fragment_container"
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout 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="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<android.support.constraint.ConstraintLayout
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -39,6 +38,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:text="@string/label_unlockedBalance"
|
||||
android:textColor="@color/colorAccent"
|
||||
android:textSize="10sp"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/tvUnlockedBalance"
|
||||
app:layout_constraintRight_toLeftOf="@+id/tvUnlockedBalance" />
|
||||
|
@ -49,6 +49,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="00000000.000000000000"
|
||||
android:textColor="@color/colorAccent"
|
||||
app:layout_constraintRight_toRightOf="@+id/tvBalance"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tvBalance" />
|
||||
|
||||
|
@ -61,7 +62,7 @@
|
|||
android:text="Loading..."
|
||||
android:textSize="10sp"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/tvBalance" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvBlockHeightProgress"
|
||||
|
@ -72,7 +73,7 @@
|
|||
android:text="Loading..."
|
||||
android:textSize="10sp"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tvConnectionStatus" />
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/tvUnlockedBalance"/>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
||||
|
@ -101,13 +102,13 @@
|
|||
android:progress="0" />
|
||||
</LinearLayout>
|
||||
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<android.support.constraint.ConstraintLayout
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<android.support.v7.widget.RecyclerView
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/list"
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
<string name="generate_title">Generate Wallet</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_buttonGenerate">Make me a wallet already!</string>
|
||||
<string name="generate_seed">Mnemonic Seed</string>
|
||||
<string name="generate_button_accept">I have noted the above\nNow, I want to loose all my money!</string>
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
|
|
30
doc/FAQ.md
30
doc/FAQ.md
|
@ -1,16 +1,28 @@
|
|||
# FAQ
|
||||
|
||||
## Do you have any screenshots of what it looks like and how it works?
|
||||
## What features does it have?
|
||||
|
||||
### [Select Wallet](images/A-wallet_selection.png)
|
||||
- Testnet and Mainnet
|
||||
- Generate new wallets
|
||||
- Recover wallets form nmemonic seed or from keys
|
||||
- Create Watch Only wallets from address + viewkey
|
||||
- Multiple wallets
|
||||
- View wallet details (address, keys, etc.)
|
||||
- View transactions inlcuding details and copy to clipboard
|
||||
- Spend Moneroj (only on testnet - someone will loose money and want to blame me. No thanks!)
|
||||
- Manually import existing wallet (by copying them to the Monerujo folder)
|
||||
|
||||
## Do you have any screenshots of what it looks like and how it works?
|
||||
Here are some old screenshots
|
||||
#### [Select Wallet](images/A-wallet_selection.png)
|
||||
|
||||
Here you see a list of installed wallets and an entry field at the top to enter the daemon address. To the right there is a pushbutton to change between testnet and mainnet. The entered daemon is saved and displayed according to the state of this button.
|
||||
|
||||
### [Wallet Password](images/B-enter_password.png)
|
||||
#### [Wallet Password](images/B-enter_password.png)
|
||||
|
||||
After selecting the wallet, the password is entered.
|
||||
|
||||
### [Wallet Syncing](images/C-wallet_syncing.png)
|
||||
#### [Wallet Syncing](images/C-wallet_syncing.png)
|
||||
|
||||
After some seconds the wallet is displyed with it's last known state and synced to the network. If it says "disconnected" or takes forever to show this screen then the entered daemon is wrong or unreachable. (Yes, I need to check the daemon availability on the login screen ...) Go back, and check that.
|
||||
|
||||
|
@ -22,15 +34,9 @@ The balance is updated during sync.
|
|||
|
||||
When the blockchain is synced, the screen shows "Synced" and the current blockchain height. When new blocks become available they are also synced and new transactions are displayed.
|
||||
|
||||
## What features does it have?
|
||||
|
||||
That's about it. Select a wallet and show the balance. Behind the scenes it keeps in sync with the blockchain while the app is open. So currently it is a view only wallet. You can use it to monitor your wallets on the go.
|
||||
|
||||
In future it will have the possibility of executing transactions. And generating wallets. Technically, it can generate wallets now, but they are pointless since you need another client to make transactions anyway - so you can make the wallets on the other client.
|
||||
|
||||
## What files do I need to copy?
|
||||
|
||||
You need to copy the wallet files from you current Monero client. These are:
|
||||
If you want to use existing wallet files, you need to copy the wallet files from you current Monero client. These are:
|
||||
```
|
||||
WalletName
|
||||
WalletName.address.txt
|
||||
|
@ -43,4 +49,4 @@ This depends on your installation - you could search for them in your home direc
|
|||
|
||||
### What if don't have these files?
|
||||
|
||||
As this is a view-only App right now, you need another client for generating wallets and sending transactions. This will change soon<sup>TM</sup>.
|
||||
Keep calm and make a new wallet.
|
||||
|
|
Loading…
Reference in New Issue