cleanup + docs

This commit is contained in:
m2049r 2017-08-20 16:18:59 +02:00
parent 37414be4bf
commit 30e35a895d
18 changed files with 160 additions and 118 deletions

1
.idea/.gitignore vendored
View File

@ -1 +1,2 @@
workspace.xml
markdown-navigator*

View File

@ -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:

View File

@ -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" />

View File

@ -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 :)

View File

@ -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");

View File

@ -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");

View File

@ -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);
}
}
}

View File

@ -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);

View File

@ -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]);

View File

@ -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();
}

View File

@ -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) {

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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>

View File

@ -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>

View File

@ -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.