From 01e7693425987fa70da56336174155b77480e4e1 Mon Sep 17 00:00:00 2001 From: m2049r Date: Mon, 18 Jun 2018 09:43:32 +0200 Subject: [PATCH] subaddresses (#322) --- app/src/main/cpp/monerujo.cpp | 60 ++++++++++--- .../com/m2049r/xmrwallet/ReceiveFragment.java | 87 ++++++++++++++++--- .../java/com/m2049r/xmrwallet/TxFragment.java | 9 +- .../com/m2049r/xmrwallet/WalletActivity.java | 15 ++-- .../layout/TransactionInfoAdapter.java | 6 +- .../xmrwallet/model/TransactionHistory.java | 2 +- .../xmrwallet/model/TransactionInfo.java | 15 ++-- .../com/m2049r/xmrwallet/model/Wallet.java | 58 +++++++++++-- .../res/drawable/ic_settings_gray_24dp.xml | 9 ++ app/src/main/res/layout/fragment_receive.xml | 50 ++++++++--- app/src/main/res/values-de/strings.xml | 2 + app/src/main/res/values-es/strings.xml | 2 + app/src/main/res/values-fr/strings.xml | 2 + app/src/main/res/values-it/strings.xml | 2 + app/src/main/res/values-nb/strings.xml | 2 + app/src/main/res/values-zh-rCN/strings.xml | 2 + app/src/main/res/values-zh-rTW/strings.xml | 2 + app/src/main/res/values/strings.xml | 3 + 18 files changed, 268 insertions(+), 60 deletions(-) create mode 100644 app/src/main/res/drawable/ic_settings_gray_24dp.xml diff --git a/app/src/main/cpp/monerujo.cpp b/app/src/main/cpp/monerujo.cpp index 1e1a045..8d8e8cc 100644 --- a/app/src/main/cpp/monerujo.cpp +++ b/app/src/main/cpp/monerujo.cpp @@ -552,9 +552,11 @@ Java_com_m2049r_xmrwallet_model_Wallet_setPassword(JNIEnv *env, jobject instance JNIEXPORT jstring JNICALL Java_com_m2049r_xmrwallet_model_Wallet_getAddressJ(JNIEnv *env, jobject instance, - jint accountIndex) { + jint accountIndex, + jint addressIndex) { Bitmonero::Wallet *wallet = getHandle(env, instance); - return env->NewStringUTF(wallet->address((uint32_t) accountIndex).c_str()); + return env->NewStringUTF( + wallet->address((uint32_t) accountIndex, (uint32_t) addressIndex).c_str()); } JNIEXPORT jstring JNICALL @@ -850,10 +852,10 @@ Java_com_m2049r_xmrwallet_model_Wallet_createTransactionJ(JNIEnv *env, jobject i JNIEXPORT jlong JNICALL Java_com_m2049r_xmrwallet_model_Wallet_createSweepTransaction(JNIEnv *env, jobject instance, - jstring dst_addr, jstring payment_id, - jint mixin_count, - jint priority, - jint accountIndex) { + jstring dst_addr, jstring payment_id, + jint mixin_count, + jint priority, + jint accountIndex) { const char *_dst_addr = env->GetStringUTFChars(dst_addr, NULL); const char *_payment_id = env->GetStringUTFChars(payment_id, NULL); @@ -988,7 +990,7 @@ Java_com_m2049r_xmrwallet_model_Wallet_getTxKey(JNIEnv *env, jobject instance, //virtual void addSubaddressAccount(const std::string& label) = 0; JNIEXPORT void JNICALL Java_com_m2049r_xmrwallet_model_Wallet_addAccount(JNIEnv *env, jobject instance, - jstring label) { + jstring label) { const char *_label = env->GetStringUTFChars(label, NULL); @@ -1027,13 +1029,44 @@ Java_com_m2049r_xmrwallet_model_Wallet_setSubaddressLabel(JNIEnv *env, jobject i // virtual size_t numSubaddressAccounts() const = 0; JNIEXPORT jint JNICALL -Java_com_m2049r_xmrwallet_model_Wallet_numSubaddressAccounts(JNIEnv *env, jobject instance) { +Java_com_m2049r_xmrwallet_model_Wallet_getNumAccounts(JNIEnv *env, jobject instance) { Bitmonero::Wallet *wallet = getHandle(env, instance); - return wallet->numSubaddressAccounts(); + return static_cast(wallet->numSubaddressAccounts()); } +//virtual size_t numSubaddresses(uint32_t accountIndex) const = 0; +JNIEXPORT jint JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getNumSubaddresses(JNIEnv *env, jobject instance, + jint accountIndex) { + Bitmonero::Wallet *wallet = getHandle(env, instance); + return static_cast(wallet->numSubaddresses(accountIndex)); +} +//virtual void addSubaddress(uint32_t accountIndex, const std::string &label) = 0; +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_addSubaddress(JNIEnv *env, jobject instance, + jint accountIndex, + jstring label) { + const char *_label = env->GetStringUTFChars(label, NULL); + Bitmonero::Wallet *wallet = getHandle(env, instance); + wallet->addSubaddress(accountIndex, _label); + env->ReleaseStringUTFChars(label, _label); +} + +/*JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getLastSubaddress(JNIEnv *env, jobject instance, + jint accountIndex) { + + Bitmonero::Wallet *wallet = getHandle(env, instance); + size_t num = wallet->numSubaddresses(accountIndex); + //wallet->subaddress()->getAll()[num]->getAddress().c_str() + Monero::Subaddress *s = wallet->subaddress(); + s->refresh(accountIndex); + std::vector v = s->getAll(); + return env->NewStringUTF(v[num - 1]->getAddress().c_str()); +} +*/ //virtual std::string signMessage(const std::string &message) = 0; //virtual bool verifySignedMessage(const std::string &message, const std::string &addres, const std::string &signature) const = 0; @@ -1079,10 +1112,13 @@ jobject newTransferList(JNIEnv *env, Bitmonero::TransactionInfo *info) { jobject newTransactionInfo(JNIEnv *env, Bitmonero::TransactionInfo *info) { jmethodID c = env->GetMethodID(class_TransactionInfo, "", - "(IZZJJJLjava/lang/String;JLjava/lang/String;IJLjava/util/List;)V"); + "(IZZJJJLjava/lang/String;JLjava/lang/String;IIJLjava/util/List;)V"); jobject transfers = newTransferList(env, info); jstring _hash = env->NewStringUTF(info->hash().c_str()); jstring _paymentId = env->NewStringUTF(info->paymentId().c_str()); + uint32_t subaddrIndex = 0; + if (info->direction() == Bitmonero::TransactionInfo::Direction_In) + subaddrIndex = *(info->subaddrIndex().begin()); jobject result = env->NewObject(class_TransactionInfo, c, info->direction(), info->isPending(), @@ -1094,6 +1130,7 @@ jobject newTransactionInfo(JNIEnv *env, Bitmonero::TransactionInfo *info) { static_cast (info->timestamp()), _paymentId, info->subaddrAccount(), + subaddrIndex, info->confirmations(), transfers); env->DeleteLocalRef(transfers); @@ -1104,6 +1141,7 @@ jobject newTransactionInfo(JNIEnv *env, Bitmonero::TransactionInfo *info) { #include #include + jobject cpp2java(JNIEnv *env, std::vector vector) { jmethodID java_util_ArrayList_ = env->GetMethodID(class_ArrayList, "", "(I)V"); @@ -1161,11 +1199,13 @@ Java_com_m2049r_xmrwallet_model_PendingTransaction_getAmount(JNIEnv *env, jobjec Bitmonero::PendingTransaction *tx = getHandle(env, instance); return tx->amount(); } + JNIEXPORT jlong JNICALL Java_com_m2049r_xmrwallet_model_PendingTransaction_getDust(JNIEnv *env, jobject instance) { Bitmonero::PendingTransaction *tx = getHandle(env, instance); return tx->dust(); } + JNIEXPORT jlong JNICALL Java_com_m2049r_xmrwallet_model_PendingTransaction_getFee(JNIEnv *env, jobject instance) { Bitmonero::PendingTransaction *tx = getHandle(env, instance); diff --git a/app/src/main/java/com/m2049r/xmrwallet/ReceiveFragment.java b/app/src/main/java/com/m2049r/xmrwallet/ReceiveFragment.java index 2934baa..7e5f501 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/ReceiveFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/ReceiveFragment.java @@ -62,6 +62,7 @@ import timber.log.Timber; public class ReceiveFragment extends Fragment { private ProgressBar pbProgress; + private TextView tvAddressLabel; private TextView tvAddress; private TextInputLayout etPaymentId; private ExchangeView evAmount; @@ -71,6 +72,10 @@ public class ReceiveFragment extends Fragment { private ImageView qrCodeFull; private EditText etDummy; private ImageButton bCopyAddress; + private Button bSubaddress; + + private Wallet wallet = null; + private boolean isMyWallet = false; public interface Listener { void setToolbarButton(int type); @@ -87,6 +92,7 @@ public class ReceiveFragment extends Fragment { View view = inflater.inflate(R.layout.fragment_receive, container, false); pbProgress = (ProgressBar) view.findViewById(R.id.pbProgress); + tvAddressLabel = (TextView) view.findViewById(R.id.tvAddressLabel); tvAddress = (TextView) view.findViewById(R.id.tvAddress); etPaymentId = (TextInputLayout) view.findViewById(R.id.etPaymentId); evAmount = (ExchangeView) view.findViewById(R.id.evAmount); @@ -96,6 +102,7 @@ public class ReceiveFragment extends Fragment { qrCodeFull = (ImageView) view.findViewById(R.id.qrCodeFull); etDummy = (EditText) view.findViewById(R.id.etDummy); bCopyAddress = (ImageButton) view.findViewById(R.id.bCopyAddress); + bSubaddress = (Button) view.findViewById(R.id.bSubaddress); etPaymentId.getEditText().setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); @@ -162,6 +169,18 @@ public class ReceiveFragment extends Fragment { } }); + bSubaddress.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + enableSubaddressButton(false); + tvAddress.setText(wallet.getNewSubaddress()); + tvAddressLabel.setText(getString(R.string.generate_address_label_sub, + wallet.getNumSubaddresses() - 1)); + storeWallet(); + generateQr(); + } + }); + qrCode.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -194,11 +213,25 @@ public class ReceiveFragment extends Fragment { String password = b.getString("password"); loadAndShow(path, password); } else { - show(walletName, address); + if (getActivity() instanceof GenerateReviewFragment.ListenerWithWallet) { + wallet = ((GenerateReviewFragment.ListenerWithWallet) getActivity()).getWallet(); + show(); + } else { + throw new IllegalStateException("no wallet info"); + } } return view; } + void enableSubaddressButton(boolean enable) { + bSubaddress.setEnabled(enable); + if (enable) { + bSubaddress.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.ic_settings_orange_24dp,0,0); + } else { + bSubaddress.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.ic_settings_gray_24dp,0,0); + } + } + void copyAddress() { Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_address), tvAddress.getText().toString()); Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show(); @@ -228,17 +261,17 @@ public class ReceiveFragment extends Fragment { super.onResume(); Timber.d("onResume()"); listenerCallback.setToolbarButton(Toolbar.BUTTON_BACK); - listenerCallback.setSubtitle(getString(R.string.receive_title)); + listenerCallback.setSubtitle(wallet.getAccountLabel()); generateQr(); } private boolean isLoaded = false; - private void show(String name, String address) { - Timber.d("name=%s", name); + private void show() { + Timber.d("name=%s", wallet.getName()); isLoaded = true; - listenerCallback.setTitle(name); - tvAddress.setText(address); + listenerCallback.setTitle(wallet.getName()); + tvAddress.setText(wallet.getAddress()); etPaymentId.setEnabled(true); bPaymentId.setEnabled(true); bCopyAddress.setClickable(true); @@ -254,18 +287,14 @@ public class ReceiveFragment extends Fragment { private class AsyncShow extends AsyncTask { String password; - String address; - String name; @Override protected Boolean doInBackground(String... params) { if (params.length != 2) return false; String walletPath = params[0]; password = params[1]; - Wallet wallet = WalletManager.getInstance().openWallet(walletPath, password); - address = wallet.getAddress(); - name = wallet.getName(); - wallet.close(); + wallet = WalletManager.getInstance().openWallet(walletPath, password); + isMyWallet = true; return true; } @@ -274,7 +303,7 @@ public class ReceiveFragment extends Fragment { super.onPostExecute(result); if (!isAdded()) return; // never mind if (result) { - show(name, address); + show(); } else { Toast.makeText(getActivity(), getString(R.string.receive_cannot_open), Toast.LENGTH_LONG).show(); hideProgress(); @@ -282,6 +311,27 @@ public class ReceiveFragment extends Fragment { } } + private void storeWallet() { + new AsyncStore().executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR); + } + + private class AsyncStore extends AsyncTask { + + @Override + protected Boolean doInBackground(String... params) { + if (params.length != 0) return false; + if (wallet != null) wallet.store(); + return true; + } + + @Override + protected void onPostExecute(Boolean result) { + enableSubaddressButton(true); + super.onPostExecute(result); + } + } + + private boolean checkPaymentId() { String paymentId = etPaymentId.getEditText().getText().toString(); boolean ok = paymentId.isEmpty() || Wallet.isPaymentIdValid(paymentId); @@ -418,4 +468,15 @@ public class ReceiveFragment extends Fragment { Timber.d("onPause()"); super.onPause(); } + + @Override + public void onDetach() { + Timber.d("onDetach()"); + if ((wallet != null) && (isMyWallet)) { + wallet.close(); + wallet = null; + isMyWallet = false; + } + super.onDetach(); + } } diff --git a/app/src/main/java/com/m2049r/xmrwallet/TxFragment.java b/app/src/main/java/com/m2049r/xmrwallet/TxFragment.java index 5cc7aa9..ea86aa5 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/TxFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/TxFragment.java @@ -224,7 +224,7 @@ public class TxFragment extends Fragment { activityCallback.setSubtitle(getString(R.string.tx_title)); activityCallback.setToolbarButton(Toolbar.BUTTON_BACK); - tvAccount.setText("" + info.subaddrAccount); + tvAccount.setText(getString(R.string.tx_account_formatted, info.account, info.subaddress)); tvTxTimestamp.setText(TS_FORMATTER.format(new Date(info.timestamp * 1000))); tvTxId.setText(info.hash); @@ -287,7 +287,10 @@ public class TxFragment extends Fragment { } } else { sb.append("-"); - dstSb.append(info.direction == TransactionInfo.Direction.Direction_In ? activityCallback.getWalletAddress() : "-"); + dstSb.append(info.direction == + TransactionInfo.Direction.Direction_In ? + activityCallback.getWalletSubaddress(info.account, info.subaddress) : + "-"); } tvTxTransfers.setText(sb.toString()); tvDestination.setText(dstSb.toString()); @@ -322,7 +325,7 @@ public class TxFragment extends Fragment { Listener activityCallback; public interface Listener { - String getWalletAddress(); + String getWalletSubaddress(int accountIndex, int subaddressIndex); String getTxKey(String hash); diff --git a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java index f382af0..5df749a 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java @@ -491,8 +491,8 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis @Override public boolean onRefreshed(final Wallet wallet, final boolean full) { Timber.d("onRefreshed()"); - if (numAccounts != wallet.numAccounts()) { - numAccounts = wallet.numAccounts(); + if (numAccounts != wallet.getNumAccounts()) { + numAccounts = wallet.getNumAccounts(); runOnUiThread(new Runnable() { public void run() { updateAccountsList(); @@ -740,8 +740,8 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis } @Override - public String getWalletAddress() { - return getWallet().getAddress(); + public String getWalletSubaddress(int accountIndex, int subaddressIndex) { + return getWallet().getSubaddress(accountIndex, subaddressIndex); } public String getWalletName() { @@ -891,7 +891,7 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis @Override public void onWalletReceive() { - startReceive(getWalletAddress()); + startReceive(getWallet().getAddress()); } void startReceive(String address) { @@ -966,7 +966,8 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis Helper.getDisplayAmount(wallet.getBalanceAll(), 5))); Menu menu = accountsView.getMenu(); menu.removeGroup(R.id.accounts_list); - for (int i = 0; i < wallet.numAccounts(); i++) { + final int n = wallet.getNumAccounts(); + for (int i = 0; i < n; i++) { final String label = wallet.getAccountLabel(i); final MenuItem item = menu.add(R.id.accounts_list, getAccountId(i), 2 * i, label); item.setIcon(R.drawable.ic_account_balance_wallet_black_24dp); @@ -1050,7 +1051,7 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis switch (id) { case R.id.account_new: getWallet().addAccount(); - int newIdx = getWallet().numAccounts() - 1; + int newIdx = getWallet().getNumAccounts() - 1; getWallet().setAccountIndex(newIdx); Toast.makeText(this, getString(R.string.accounts_new, newIdx), diff --git a/app/src/main/java/com/m2049r/xmrwallet/layout/TransactionInfoAdapter.java b/app/src/main/java/com/m2049r/xmrwallet/layout/TransactionInfoAdapter.java index c739c08..0fa4109 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/layout/TransactionInfoAdapter.java +++ b/app/src/main/java/com/m2049r/xmrwallet/layout/TransactionInfoAdapter.java @@ -166,7 +166,11 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter iterator = t.iterator(); iterator.hasNext(); ) { TransactionInfo info = iterator.next(); - if (info.subaddrAccount != accountIndex) { + if (info.account != accountIndex) { iterator.remove(); Timber.d("removed %s", info.hash); } else { diff --git a/app/src/main/java/com/m2049r/xmrwallet/model/TransactionInfo.java b/app/src/main/java/com/m2049r/xmrwallet/model/TransactionInfo.java index bf45a2d..74ada2c 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/model/TransactionInfo.java +++ b/app/src/main/java/com/m2049r/xmrwallet/model/TransactionInfo.java @@ -67,7 +67,8 @@ public class TransactionInfo implements Parcelable, Comparable public String hash; public long timestamp; public String paymentId; - public int subaddrAccount; + public int account; + public int subaddress; public long confirmations; public List transfers; @@ -84,7 +85,8 @@ public class TransactionInfo implements Parcelable, Comparable String hash, long timestamp, String paymentId, - int subaddrAccount, + int account, + int subaddress, long confirmations, List transfers) { this.direction = Direction.values()[direction]; @@ -96,7 +98,8 @@ public class TransactionInfo implements Parcelable, Comparable this.hash = hash; this.timestamp = timestamp; this.paymentId = paymentId; - this.subaddrAccount = subaddrAccount; + this.account = account; + this.subaddress = subaddress; this.confirmations = confirmations; this.transfers = transfers; } @@ -116,7 +119,8 @@ public class TransactionInfo implements Parcelable, Comparable out.writeString(hash); out.writeLong(timestamp); out.writeString(paymentId); - out.writeInt(subaddrAccount); + out.writeInt(account); + out.writeInt(subaddress); out.writeLong(confirmations); out.writeList(transfers); out.writeString(txKey); @@ -143,7 +147,8 @@ public class TransactionInfo implements Parcelable, Comparable hash = in.readString(); timestamp = in.readLong(); paymentId = in.readString(); - subaddrAccount = in.readInt(); + account = in.readInt(); + subaddress = in.readInt(); confirmations = in.readLong(); transfers = in.readArrayList(Transfer.class.getClassLoader()); txKey = in.readString(); diff --git a/app/src/main/java/com/m2049r/xmrwallet/model/Wallet.java b/app/src/main/java/com/m2049r/xmrwallet/model/Wallet.java index 09dcdca..cdc0f8f 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/model/Wallet.java +++ b/app/src/main/java/com/m2049r/xmrwallet/model/Wallet.java @@ -19,6 +19,9 @@ package com.m2049r.xmrwallet.model; import com.m2049r.xmrwallet.data.TxData; import java.io.File; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; import timber.log.Timber; @@ -86,10 +89,22 @@ public class Wallet { public native boolean setPassword(String password); public String getAddress() { - return getAddressJ(accountIndex); + return getAddress(accountIndex); } - private native String getAddressJ(int accountIndex); + public String getAddress(int accountIndex) { + return getAddressJ(accountIndex, 0); + } + + public String getSubaddress(int addressIndex) { + return getAddressJ(accountIndex, addressIndex); + } + + public String getSubaddress(int accountIndex, int addressIndex) { + return getAddressJ(accountIndex, addressIndex); + } + + private native String getAddressJ(int accountIndex, int addressIndex); public native String getPath(); @@ -109,7 +124,9 @@ public class Wallet { public native String getSecretSpendKey(); public boolean store() { - return store(""); + final boolean ok = store(""); + Timber.d("stored"); + return ok; } public native boolean store(String path); @@ -323,17 +340,21 @@ public class Wallet { public String getAccountLabel(int accountIndex) { String label = getSubaddressLabel(accountIndex, 0); if (label.equals(NEW_ACCOUNT_NAME)) { - String address = getAddressJ(accountIndex); + String address = getAddress(accountIndex); int len = address.length(); return address.substring(0, 6) + "\u2026" + address.substring(len - 6, len); } else return label; } + public String getSubaddressLabel(int addressIndex) { + return getSubaddressLabel(accountIndex, addressIndex); + } + public native String getSubaddressLabel(int accountIndex, int addressIndex); public void setAccountLabel(String label) { - setSubaddressLabel(accountIndex, 0, label); + setAccountLabel(accountIndex, label); } public void setAccountLabel(int accountIndex, String label) { @@ -342,9 +363,30 @@ public class Wallet { public native void setSubaddressLabel(int accountIndex, int addressIndex, String label); - public int numAccounts() { - return numSubaddressAccounts(); + public native int getNumAccounts(); + + public int getNumSubaddresses() { + return getNumSubaddresses(accountIndex); + } + + public native int getNumSubaddresses(int accountIndex); + + public String getNewSubaddress() { + return getNewSubaddress(accountIndex); + } + + public String getNewSubaddress(int accountIndex) { + String timeStamp = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss", Locale.US).format(new Date()); + addSubaddress(accountIndex, timeStamp); + String subaddress = getLastSubaddress(accountIndex); + Timber.d("%d: %s", getNumSubaddresses(accountIndex) - 1, subaddress); + return subaddress; + } + + public native void addSubaddress(int accountIndex, String label); + + public String getLastSubaddress(int accountIndex) { + return getSubaddress(accountIndex, getNumSubaddresses(accountIndex) - 1); } - public native int numSubaddressAccounts(); } diff --git a/app/src/main/res/drawable/ic_settings_gray_24dp.xml b/app/src/main/res/drawable/ic_settings_gray_24dp.xml new file mode 100644 index 0000000..42e98f0 --- /dev/null +++ b/app/src/main/res/drawable/ic_settings_gray_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/fragment_receive.xml b/app/src/main/res/layout/fragment_receive.xml index 02d3b8c..3e3e4ff 100644 --- a/app/src/main/res/layout/fragment_receive.xml +++ b/app/src/main/res/layout/fragment_receive.xml @@ -24,11 +24,13 @@ android:indeterminate="true" android:visibility="gone" /> - + android:layout_height="wrap_content" + android:gravity="center"> - + - + android:layout_marginTop="8dp" + android:orientation="horizontal" + android:weightSum="10"> + + + +