Use NFC for receive & send (#380)

* Use NFC Tag for receive & send

* prep translations
This commit is contained in:
m2049r 2018-08-06 22:18:57 +02:00 committed by GitHub
parent a9a78393a9
commit 1433143a39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 537 additions and 95 deletions

View File

@ -7,8 +7,8 @@ android {
applicationId "com.m2049r.xmrwallet"
minSdkVersion 21
targetSdkVersion 25
versionCode 111
versionName "1.6.1 'Nano S'"
versionCode 112
versionName "1.6.2 'Nano S'"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {

View File

@ -8,10 +8,11 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.NFC" />
<application
android:name=".XmrWalletApplication"
android:allowBackup="true"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
@ -41,32 +42,12 @@
<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/usb_device_filter" />
</activity>
<!--activity
android:name=".util.UsbEventReceiverActivity"
android:excludeFromRecents="true"
android:exported="false"
android:label="@string/app_name"
android:noHistory="true"
android:process=":UsbEventReceiverActivityProcess"
android:taskAffinity="com.m2049r.xmrwallet.taskAffinityUsbEventReceiver"
android:theme="@style/Theme.Transparent">
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/usb_device_filter" />
</activity-->
<service
android:name=".service.WalletService"
android:description="@string/service_description"
android:exported="false"
android:label="Monero Wallet Service" />
</application>
</manifest>

View File

@ -1,14 +1,33 @@
package com.m2049r.xmrwallet;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import android.support.annotation.CallSuper;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.widget.Toast;
import com.m2049r.xmrwallet.data.BarcodeData;
import com.m2049r.xmrwallet.dialog.ProgressDialog;
import com.m2049r.xmrwallet.fragment.send.SendFragment;
import com.m2049r.xmrwallet.ledger.Ledger;
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
import java.io.IOException;
import timber.log.Timber;
public class BaseActivity extends SecureActivity implements GenerateReviewFragment.ProgressListener {
@ -102,4 +121,174 @@ public class BaseActivity extends SecureActivity implements GenerateReviewFragme
wl = null;
Timber.d("WakeLock released");
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initNfc();
}
@Override
protected void onPostResume() {
super.onPostResume();
if (nfcAdapter != null) {
nfcAdapter.enableForegroundDispatch(this, nfcPendingIntent, null, null);
// intercept all techs so we can tell the user their tag is no good
}
}
@Override
protected void onPause() {
Timber.d("onPause()");
if (nfcAdapter != null)
nfcAdapter.disableForegroundDispatch(this);
super.onPause();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
processNfcIntent(intent);
}
// NFC stuff
private NfcAdapter nfcAdapter;
private PendingIntent nfcPendingIntent;
public void initNfc() {
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (nfcAdapter == null) // no NFC support
return;
nfcPendingIntent = PendingIntent.getActivity(this, 0,
new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
0);
}
private void processNfcIntent(Intent intent) {
String action = intent.getAction();
Timber.d("ACTION=%s", action);
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
Ndef ndef = Ndef.get(tag);
if (ndef == null) {
Toast.makeText(this, getString(R.string.nfc_tag_unsupported), Toast.LENGTH_LONG).show();
return;
}
Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if (f instanceof ReceiveFragment) {
// We want to write a Tag from the ReceiveFragment
BarcodeData bc = ((ReceiveFragment) f).getBarcodeData();
if (bc != null) {
new AsyncWriteTag(ndef, bc.getUri()).execute();
} // else wallet is not loaded yet or receive is otherwise not ready - ignore
} else if (f instanceof SendFragment) {
// We want to read a Tag for the SendFragment
NdefMessage ndefMessage = ndef.getCachedNdefMessage();
if (ndefMessage == null) {
Toast.makeText(this, getString(R.string.nfc_tag_read_undef), Toast.LENGTH_LONG).show();
return;
}
NdefRecord firstRecord = ndefMessage.getRecords()[0];
Uri uri = firstRecord.toUri(); // we insist on the first record
if (uri == null) {
Toast.makeText(this, getString(R.string.nfc_tag_read_undef), Toast.LENGTH_LONG).show();
} else {
BarcodeData bc = BarcodeData.fromQrCode(uri.toString());
if (bc == null)
Toast.makeText(this, getString(R.string.nfc_tag_read_undef), Toast.LENGTH_LONG).show();
else
onUriScanned(bc);
}
}
}
}
// this gets called only if we get data
@CallSuper
void onUriScanned(BarcodeData barcodeData) {
// do nothing by default yet
}
private BarcodeData barcodeData = null;
private BarcodeData popBarcodeData() {
BarcodeData popped = barcodeData;
barcodeData = null;
return popped;
}
private class AsyncWriteTag extends AsyncTask<Void, Void, Boolean> {
Ndef ndef;
Uri uri;
String errorMessage = null;
AsyncWriteTag(Ndef ndef, Uri uri) {
this.ndef = ndef;
this.uri = uri;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
showProgressDialog(R.string.progress_nfc_write);
}
@Override
protected Boolean doInBackground(Void... params) {
if (params.length != 0) return false;
try {
writeNdef(ndef, uri);
return true;
} catch (IOException | FormatException ex) {
Timber.e(ex);
} catch (IllegalArgumentException ex) {
errorMessage = ex.getMessage();
Timber.d(errorMessage);
} finally {
try {
ndef.close();
} catch (IOException ex) {
Timber.e(ex);
}
}
return false;
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (isDestroyed()) {
return;
}
dismissProgressDialog();
if (!result) {
if (errorMessage != null)
Toast.makeText(getApplicationContext(), errorMessage, Toast.LENGTH_LONG).show();
else
Toast.makeText(getApplicationContext(), getString(R.string.nfc_write_failed), Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getApplicationContext(), getString(R.string.nfc_write_successful), Toast.LENGTH_SHORT).show();
}
}
}
void writeNdef(Ndef ndef, Uri uri) throws IOException, FormatException {
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (nfcAdapter == null) return; // no NFC support here
NdefRecord recordNFC = NdefRecord.createUri(uri);
NdefMessage message = new NdefMessage(recordNFC);
ndef.connect();
int tagSize = ndef.getMaxSize();
int msgSize = message.getByteArrayLength();
Timber.d("tagSize=%d, msgSIze=%d, uriSize=%d", tagSize, msgSize, uri.toString().length());
if (tagSize < msgSize)
throw new IllegalArgumentException(getString(R.string.nfc_tag_size, tagSize, msgSize));
ndef.writeNdefMessage(message);
}
}

View File

@ -149,8 +149,7 @@ public class LoginActivity extends BaseActivity
} else {
Timber.i("Waiting for permissions");
}
processIntent(getIntent());
processUsbIntent(getIntent());
}
boolean checkServiceRunning() {
@ -1255,10 +1254,10 @@ public class LoginActivity extends BaseActivity
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
processIntent(intent);
processUsbIntent(intent);
}
private void processIntent(Intent intent) {
private void processUsbIntent(Intent intent) {
String action = intent.getAction();
if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
synchronized (this) {

View File

@ -0,0 +1,23 @@
/*
* 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 com.m2049r.xmrwallet.data.BarcodeData;
public interface OnUriScannedListener {
boolean onUriScanned(BarcodeData barcodeData);
}

View File

@ -20,6 +20,8 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.nfc.NfcAdapter;
import android.nfc.NfcManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.design.widget.TextInputLayout;
@ -226,6 +228,12 @@ public class ReceiveFragment extends Fragment {
throw new IllegalStateException("no wallet info");
}
}
View tvNfc = view.findViewById(R.id.tvNfc);
NfcManager manager = (NfcManager) getContext().getSystemService(Context.NFC_SERVICE);
if ((manager != null) && (manager.getDefaultAdapter() != null))
tvNfc.setVisibility(View.VISIBLE);
return view;
}
@ -257,7 +265,7 @@ public class ReceiveFragment extends Fragment {
void setQR(Bitmap qr) {
qrCode.setImageBitmap(qr);
qrValid = true;
tvQrCode.setVisibility(View.INVISIBLE);
tvQrCode.setVisibility(View.GONE);
Helper.hideKeyboard(getActivity());
etDummy.requestFocus();
}
@ -373,7 +381,6 @@ public class ReceiveFragment extends Fragment {
}
}
private boolean checkPaymentId() {
String paymentId = etPaymentId.getEditText().getText().toString();
boolean ok = paymentId.isEmpty() || Wallet.isPaymentIdValid(paymentId);
@ -386,6 +393,15 @@ public class ReceiveFragment extends Fragment {
return ok;
}
public BarcodeData getBarcodeData() {
if (qrValid)
return bcData;
else
return null;
}
private BarcodeData bcData = null;
private void generateQr() {
Timber.d("GENQR");
String address = tvAddress.getText().toString();
@ -397,27 +413,9 @@ public class ReceiveFragment extends Fragment {
Timber.d("CLEARQR");
return;
}
StringBuffer sb = new StringBuffer();
sb.append(BarcodeData.XMR_SCHEME).append(address);
boolean first = true;
if (!paymentId.isEmpty()) {
if (first) {
sb.append("?");
first = false;
}
sb.append(BarcodeData.XMR_PAYMENTID).append('=').append(paymentId);
}
if (!xmrAmount.isEmpty()) {
if (first) {
sb.append("?");
} else {
sb.append("&");
}
sb.append(BarcodeData.XMR_AMOUNT).append('=').append(xmrAmount);
}
String text = sb.toString();
bcData = new BarcodeData(BarcodeData.Asset.XMR, address, paymentId, xmrAmount);
int size = Math.min(qrCode.getHeight(), qrCode.getWidth());
Bitmap qr = generate(text, size, size);
Bitmap qr = generate(bcData.getUriString(), size, size);
if (qr != null) {
setQR(qr);
Timber.d("SETQR");

View File

@ -27,7 +27,6 @@ import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PowerManager;
import android.support.annotation.NonNull;
import android.support.design.widget.NavigationView;
import android.support.v4.app.Fragment;
@ -841,26 +840,39 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
}
private BarcodeData scannedData = null;
@Override
public boolean onScanned(String qrCode) {
// #gurke
BarcodeData bcData = BarcodeData.fromQrCode(qrCode);
if (bcData != null) {
this.scannedData = bcData;
popFragmentStack(null);
Timber.d("AAA");
onUriScanned(bcData);
return true;
} else {
return false;
}
}
OnUriScannedListener onUriScannedListener = null;
@Override
public BarcodeData popScannedData() {
BarcodeData data = scannedData;
scannedData = null;
return data;
public void setOnUriScannedListener(OnUriScannedListener onUriScannedListener) {
this.onUriScannedListener = onUriScannedListener;
}
@Override
void onUriScanned(BarcodeData barcodeData) {
super.onUriScanned(barcodeData);
boolean processed = false;
if (onUriScannedListener != null) {
processed = onUriScannedListener.onUriScanned(barcodeData);
}
if (!processed || (onUriScannedListener == null)) {
Toast.makeText(this, getString(R.string.nfc_tag_read_what), Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, getString(R.string.nfc_tag_read_success), Toast.LENGTH_SHORT).show();
}
}
@Override

View File

@ -18,9 +18,7 @@ package com.m2049r.xmrwallet.data;
import android.net.Uri;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.BitcoinAddressValidator;
import java.util.HashMap;
@ -45,6 +43,11 @@ public class BarcodeData {
public String paymentId = null;
public String amount = null;
public BarcodeData(String uri) {
this.asset = asset;
this.address = address;
}
public BarcodeData(Asset asset, String address) {
this.asset = asset;
this.address = address;
@ -63,6 +66,31 @@ public class BarcodeData {
this.amount = amount;
}
public Uri getUri() {
return Uri.parse(getUriString());
}
public String getUriString() {
if (asset != Asset.XMR) throw new IllegalStateException("We can only do XMR stuff!");
StringBuilder sb = new StringBuilder();
sb.append(BarcodeData.XMR_SCHEME).append(address);
boolean first = true;
if ((paymentId != null) && !paymentId.isEmpty()) {
sb.append("?");
first = false;
sb.append(BarcodeData.XMR_PAYMENTID).append('=').append(paymentId);
}
if (!amount.isEmpty()) {
if (first) {
sb.append("?");
} else {
sb.append("&");
}
sb.append(BarcodeData.XMR_AMOUNT).append('=').append(amount);
}
return sb.toString();
}
static public BarcodeData fromQrCode(String qrCode) {
// check for monero uri
BarcodeData bcData = parseMoneroUri(qrCode);

View File

@ -17,6 +17,7 @@
package com.m2049r.xmrwallet.fragment.send;
import android.content.Context;
import android.nfc.NfcManager;
import android.os.Bundle;
import android.support.design.widget.TextInputLayout;
import android.support.v7.widget.CardView;
@ -64,6 +65,8 @@ public class SendAddressWizardFragment extends SendWizardFragment {
public interface Listener {
void setBarcodeData(BarcodeData data);
BarcodeData getBarcodeData();
void setMode(SendFragment.Mode mode);
TxData getTxData();
@ -83,8 +86,6 @@ public class SendAddressWizardFragment extends SendWizardFragment {
public interface OnScanListener {
void onScan();
BarcodeData popScannedData();
}
@Override
@ -206,6 +207,11 @@ public class SendAddressWizardFragment extends SendWizardFragment {
etDummy.requestFocus();
Helper.hideKeyboard(getActivity());
View tvNfc = view.findViewById(R.id.tvNfc);
NfcManager manager = (NfcManager) getContext().getSystemService(Context.NFC_SERVICE);
if ((manager != null) && (manager.getDefaultAdapter() != null))
tvNfc.setVisibility(View.VISIBLE);
return view;
}
@ -298,11 +304,20 @@ public class SendAddressWizardFragment extends SendWizardFragment {
public void onResume() {
super.onResume();
Timber.d("onResume");
BarcodeData data = onScanListener.popScannedData();
sendListener.setBarcodeData(data);
if (data != null) {
processScannedData();
}
public void processScannedData(BarcodeData barcodeData) {
sendListener.setBarcodeData(barcodeData);
if (isResumed())
processScannedData();
}
public void processScannedData() {
BarcodeData barcodeData = sendListener.getBarcodeData();
if (barcodeData != null) {
Timber.d("GOT DATA");
String scannedAddress = data.address;
String scannedAddress = barcodeData.address;
if (scannedAddress != null) {
etAddress.getEditText().setText(scannedAddress);
checkAddress();
@ -310,7 +325,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
etAddress.getEditText().getText().clear();
etAddress.setError(null);
}
String scannedPaymenId = data.paymentId;
String scannedPaymenId = barcodeData.paymentId;
if (scannedPaymenId != null) {
etPaymentId.getEditText().setText(scannedPaymenId);
checkPaymentId();
@ -318,7 +333,8 @@ public class SendAddressWizardFragment extends SendWizardFragment {
etPaymentId.getEditText().getText().clear();
etPaymentId.setError(null);
}
}
} else
Timber.d("barcodeData=null");
}
@Override

View File

@ -37,16 +37,15 @@ import android.widget.EditText;
import android.widget.Toast;
import com.m2049r.xmrwallet.OnBackPressedListener;
import com.m2049r.xmrwallet.OnUriScannedListener;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.data.BarcodeData;
import com.m2049r.xmrwallet.data.PendingTx;
import com.m2049r.xmrwallet.data.TxData;
import com.m2049r.xmrwallet.data.TxDataBtc;
import com.m2049r.xmrwallet.dialog.HelpFragment;
import com.m2049r.xmrwallet.layout.SpendViewPager;
import com.m2049r.xmrwallet.model.PendingTransaction;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.NodeList;
import com.m2049r.xmrwallet.util.Notice;
import com.m2049r.xmrwallet.util.UserNotes;
import com.m2049r.xmrwallet.widget.DotBar;
@ -62,7 +61,7 @@ public class SendFragment extends Fragment
SendSettingsWizardFragment.Listener,
SendConfirmWizardFragment.Listener,
SendSuccessWizardFragment.Listener,
OnBackPressedListener {
OnBackPressedListener, OnUriScannedListener {
private Listener activityCallback;
@ -86,6 +85,8 @@ public class SendFragment extends Fragment
void setTitle(String title);
void setSubtitle(String subtitle);
void setOnUriScannedListener(OnUriScannedListener onUriScannedListener);
}
private EditText etDummy;
@ -99,10 +100,6 @@ public class SendFragment extends Fragment
private Button bDone;
private View llXmrToEnabled;
private View ibXmrToInfoClose;
static private int MAX_FALLBACK = Integer.MAX_VALUE;
@Override
@ -121,7 +118,7 @@ public class SendFragment extends Fragment
arrowNext = getResources().getDrawable(R.drawable.ic_navigate_next_white_24dp);
ViewGroup llNotice = (ViewGroup) view.findViewById(R.id.llNotice);
Notice.showAll(llNotice,".*_send");
Notice.showAll(llNotice, ".*_send");
spendViewPager = (SpendViewPager) view.findViewById(R.id.pager);
pagerAdapter = new SpendPagerAdapter(getChildFragmentManager());
@ -226,13 +223,20 @@ public class SendFragment extends Fragment
Timber.d("onAttach %s", context);
super.onAttach(context);
if (context instanceof Listener) {
this.activityCallback = (Listener) context;
activityCallback = (Listener) context;
activityCallback.setOnUriScannedListener(this);
} else {
throw new ClassCastException(context.toString()
+ " must implement Listener");
}
}
@Override
public void onDetach() {
activityCallback.setOnUriScannedListener(null);
super.onDetach();
}
private SpendViewPager spendViewPager;
private SpendPagerAdapter pagerAdapter;
@ -247,6 +251,18 @@ public class SendFragment extends Fragment
}
}
@Override
public boolean onUriScanned(BarcodeData barcodeData) {
if (spendViewPager.getCurrentItem() == SpendPagerAdapter.POS_ADDRESS) {
final SendWizardFragment fragment = pagerAdapter.getFragment(SpendPagerAdapter.POS_ADDRESS);
if (fragment instanceof SendAddressWizardFragment) {
((SendAddressWizardFragment) fragment).processScannedData(barcodeData);
return true;
}
}
return false;
}
enum Mode {
XMR, BTC
}
@ -414,8 +430,14 @@ public class SendFragment extends Fragment
barcodeData = data;
}
@Override
public BarcodeData getBarcodeData() {
return barcodeData;
}
@Override
public BarcodeData popBarcodeData() {
Timber.d("POPPED");
BarcodeData data = barcodeData;
barcodeData = null;
return data;

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M20,2L4,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM20,20L4,20L4,4h16v16zM18,6h-5c-1.1,0 -2,0.9 -2,2v2.28c-0.6,0.35 -1,0.98 -1,1.72 0,1.1 0.9,2 2,2s2,-0.9 2,-2c0,-0.74 -0.4,-1.38 -1,-1.72L13,8h3v8L8,16L8,8h2L10,6L6,6v12h12L18,6z" />
</vector>

View File

@ -128,37 +128,39 @@
android:text="@string/send_generate_paymentid_hint"
android:textColor="@color/moneroGray"
android:visibility="visible" />
</LinearLayout>
<FrameLayout
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tvQrCode"
style="@style/MoneroFab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:drawablePadding="4dp"
android:drawableStart="@drawable/ic_info_outline_gray_24dp"
android:text="@string/label_receive_info_gen_qr_code"
android:textAlignment="center"
android:textSize="16sp"
android:visibility="invisible" />
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_above="@id/tvNfc"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_margin="16dp"
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
card_view:cardCornerRadius="2dp"
card_view:cardElevation="8dp"
card_view:contentPadding="4dp">
<TextView
android:id="@+id/tvQrCode"
style="@style/MoneroFab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:drawablePadding="4dp"
android:drawableStart="@drawable/ic_info_outline_gray_24dp"
android:text="@string/label_receive_info_gen_qr_code"
android:textAlignment="center"
android:textSize="16sp"
android:visibility="gone" />
<ImageView
android:id="@+id/qrCode"
android:layout_width="match_parent"
@ -166,7 +168,19 @@
android:adjustViewBounds="true"
android:background="#00000000" />
</android.support.v7.widget.CardView>
</FrameLayout>
<TextView
android:id="@+id/tvNfc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:gravity="center"
android:drawablePadding="8dp"
android:drawableStart="@drawable/ic_nfc_black_24dp"
android:text="@string/nfc_tag_tap"
android:visibility="visible" />
</RelativeLayout>
</LinearLayout>
<ImageView

View File

@ -125,6 +125,7 @@
android:layout_gravity="center"
android:layout_marginTop="32dp"
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
card_view:cardCornerRadius="2dp"
card_view:cardElevation="8dp"
@ -138,7 +139,17 @@
android:gravity="center"
android:text="@string/send_qr_hint"
android:textSize="20dp" />
</android.support.v7.widget.CardView>
<TextView
android:id="@+id/tvNfc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="8dp"
android:drawablePadding="8dp"
android:drawableStart="@drawable/ic_nfc_black_24dp"
android:gravity="center"
android:text="@string/nfc_tag_tap"
android:visibility="visible" />
</LinearLayout>

View File

@ -338,4 +338,14 @@
<string name="toast_ledger_attached">%1$s attached</string>
<string name="toast_ledger_detached">%1$s detached</string>
<string name="progress_nfc_write">Writing Tag</string>
<string name="nfc_write_failed">Writing Tag failed!</string>
<string name="nfc_write_successful">Writing Tag successful</string>
<string name="nfc_tag_unsupported">Tag does not support NDEF!</string>
<string name="nfc_tag_size">Tag provides %d bytes, but we need %d!</string>
<string name="nfc_tag_read_undef">I don\'t understand the Tag!</string>
<string name="nfc_tag_read_what">I don\'t know what you want!</string>
<string name="nfc_tag_read_success">Reading Tag successful</string>
<string name="nfc_tag_tap">NFC Available!</string>
</resources>

View File

@ -337,4 +337,14 @@
<string name="toast_ledger_attached">%1$s attached</string>
<string name="toast_ledger_detached">%1$s detached</string>
<string name="progress_nfc_write">Writing Tag</string>
<string name="nfc_write_failed">Writing Tag failed!</string>
<string name="nfc_write_successful">Writing Tag successful</string>
<string name="nfc_tag_unsupported">Tag does not support NDEF!</string>
<string name="nfc_tag_size">Tag provides %d bytes, but we need %d!</string>
<string name="nfc_tag_read_undef">I don\'t understand the Tag!</string>
<string name="nfc_tag_read_what">I don\'t know what you want!</string>
<string name="nfc_tag_read_success">Reading Tag successful</string>
<string name="nfc_tag_tap">NFC Available!</string>
</resources>

View File

@ -324,4 +324,14 @@
<string name="toast_ledger_attached">%1$s attached</string>
<string name="toast_ledger_detached">%1$s detached</string>
<string name="progress_nfc_write">Writing Tag</string>
<string name="nfc_write_failed">Writing Tag failed!</string>
<string name="nfc_write_successful">Writing Tag successful</string>
<string name="nfc_tag_unsupported">Tag does not support NDEF!</string>
<string name="nfc_tag_size">Tag provides %d bytes, but we need %d!</string>
<string name="nfc_tag_read_undef">I don\'t understand the Tag!</string>
<string name="nfc_tag_read_what">I don\'t know what you want!</string>
<string name="nfc_tag_read_success">Reading Tag successful</string>
<string name="nfc_tag_tap">NFC Available!</string>
</resources>

View File

@ -340,4 +340,14 @@
<string name="toast_ledger_attached">%1$s attached</string>
<string name="toast_ledger_detached">%1$s detached</string>
<string name="progress_nfc_write">Writing Tag</string>
<string name="nfc_write_failed">Writing Tag failed!</string>
<string name="nfc_write_successful">Writing Tag successful</string>
<string name="nfc_tag_unsupported">Tag does not support NDEF!</string>
<string name="nfc_tag_size">Tag provides %d bytes, but we need %d!</string>
<string name="nfc_tag_read_undef">I don\'t understand the Tag!</string>
<string name="nfc_tag_read_what">I don\'t know what you want!</string>
<string name="nfc_tag_read_success">Reading Tag successful</string>
<string name="nfc_tag_tap">NFC Available!</string>
</resources>

View File

@ -338,4 +338,14 @@
<string name="toast_ledger_attached">%1$s attached</string>
<string name="toast_ledger_detached">%1$s detached</string>
<string name="progress_nfc_write">Writing Tag</string>
<string name="nfc_write_failed">Writing Tag failed!</string>
<string name="nfc_write_successful">Writing Tag successful</string>
<string name="nfc_tag_unsupported">Tag does not support NDEF!</string>
<string name="nfc_tag_size">Tag provides %d bytes, but we need %d!</string>
<string name="nfc_tag_read_undef">I don\'t understand the Tag!</string>
<string name="nfc_tag_read_what">I don\'t know what you want!</string>
<string name="nfc_tag_read_success">Reading Tag successful</string>
<string name="nfc_tag_tap">NFC Available!</string>
</resources>

View File

@ -339,4 +339,14 @@
<string name="toast_ledger_attached">%1$s attached</string>
<string name="toast_ledger_detached">%1$s detached</string>
<string name="progress_nfc_write">Writing Tag</string>
<string name="nfc_write_failed">Writing Tag failed!</string>
<string name="nfc_write_successful">Writing Tag successful</string>
<string name="nfc_tag_unsupported">Tag does not support NDEF!</string>
<string name="nfc_tag_size">Tag provides %d bytes, but we need %d!</string>
<string name="nfc_tag_read_undef">I don\'t understand the Tag!</string>
<string name="nfc_tag_read_what">I don\'t know what you want!</string>
<string name="nfc_tag_read_success">Reading Tag successful</string>
<string name="nfc_tag_tap">NFC Available!</string>
</resources>

View File

@ -337,4 +337,14 @@
<string name="toast_ledger_attached">%1$s attached</string>
<string name="toast_ledger_detached">%1$s detached</string>
<string name="progress_nfc_write">Writing Tag</string>
<string name="nfc_write_failed">Writing Tag failed!</string>
<string name="nfc_write_successful">Writing Tag successful</string>
<string name="nfc_tag_unsupported">Tag does not support NDEF!</string>
<string name="nfc_tag_size">Tag provides %d bytes, but we need %d!</string>
<string name="nfc_tag_read_undef">I don\'t understand the Tag!</string>
<string name="nfc_tag_read_what">I don\'t know what you want!</string>
<string name="nfc_tag_read_success">Reading Tag successful</string>
<string name="nfc_tag_tap">NFC Available!</string>
</resources>

View File

@ -340,4 +340,14 @@
<string name="toast_ledger_attached">%1$s attached</string>
<string name="toast_ledger_detached">%1$s detached</string>
<string name="progress_nfc_write">Writing Tag</string>
<string name="nfc_write_failed">Writing Tag failed!</string>
<string name="nfc_write_successful">Writing Tag successful</string>
<string name="nfc_tag_unsupported">Tag does not support NDEF!</string>
<string name="nfc_tag_size">Tag provides %d bytes, but we need %d!</string>
<string name="nfc_tag_read_undef">I don\'t understand the Tag!</string>
<string name="nfc_tag_read_what">I don\'t know what you want!</string>
<string name="nfc_tag_read_success">Reading Tag successful</string>
<string name="nfc_tag_tap">NFC Available!</string>
</resources>

View File

@ -337,4 +337,14 @@
<string name="toast_ledger_attached">%1$s attached</string>
<string name="toast_ledger_detached">%1$s detached</string>
<string name="progress_nfc_write">Writing Tag</string>
<string name="nfc_write_failed">Writing Tag failed!</string>
<string name="nfc_write_successful">Writing Tag successful</string>
<string name="nfc_tag_unsupported">Tag does not support NDEF!</string>
<string name="nfc_tag_size">Tag provides %d bytes, but we need %d!</string>
<string name="nfc_tag_read_undef">I don\'t understand the Tag!</string>
<string name="nfc_tag_read_what">I don\'t know what you want!</string>
<string name="nfc_tag_read_success">Reading Tag successful</string>
<string name="nfc_tag_tap">NFC Available!</string>
</resources>

View File

@ -339,4 +339,14 @@
<string name="toast_ledger_attached">%1$s attached</string>
<string name="toast_ledger_detached">%1$s detached</string>
<string name="progress_nfc_write">Writing Tag</string>
<string name="nfc_write_failed">Writing Tag failed!</string>
<string name="nfc_write_successful">Writing Tag successful</string>
<string name="nfc_tag_unsupported">Tag does not support NDEF!</string>
<string name="nfc_tag_size">Tag provides %d bytes, but we need %d!</string>
<string name="nfc_tag_read_undef">I don\'t understand the Tag!</string>
<string name="nfc_tag_read_what">I don\'t know what you want!</string>
<string name="nfc_tag_read_success">Reading Tag successful</string>
<string name="nfc_tag_tap">NFC Available!</string>
</resources>

View File

@ -321,4 +321,14 @@
<string name="toast_ledger_attached">%1$s attached</string>
<string name="toast_ledger_detached">%1$s detached</string>
<string name="progress_nfc_write">Writing Tag</string>
<string name="nfc_write_failed">Writing Tag failed!</string>
<string name="nfc_write_successful">Writing Tag successful</string>
<string name="nfc_tag_unsupported">Tag does not support NDEF!</string>
<string name="nfc_tag_size">Tag provides %d bytes, but we need %d!</string>
<string name="nfc_tag_read_undef">I don\'t understand the Tag!</string>
<string name="nfc_tag_read_what">I don\'t know what you want!</string>
<string name="nfc_tag_read_success">Reading Tag successful</string>
<string name="nfc_tag_tap">NFC Available!</string>
</resources>

View File

@ -335,4 +335,14 @@
<string name="toast_ledger_attached">%1$s attached</string>
<string name="toast_ledger_detached">%1$s detached</string>
<string name="progress_nfc_write">Writing Tag</string>
<string name="nfc_write_failed">Writing Tag failed!</string>
<string name="nfc_write_successful">Writing Tag successful</string>
<string name="nfc_tag_unsupported">Tag does not support NDEF!</string>
<string name="nfc_tag_size">Tag provides %d bytes, but we need %d!</string>
<string name="nfc_tag_read_undef">I don\'t understand the Tag!</string>
<string name="nfc_tag_read_what">I don\'t know what you want!</string>
<string name="nfc_tag_read_success">Reading Tag successful</string>
<string name="nfc_tag_tap">NFC Available!</string>
</resources>

View File

@ -336,4 +336,14 @@
<string name="toast_ledger_attached">%1$s attached</string>
<string name="toast_ledger_detached">%1$s detached</string>
<string name="progress_nfc_write">Writing Tag</string>
<string name="nfc_write_failed">Writing Tag failed!</string>
<string name="nfc_write_successful">Writing Tag successful</string>
<string name="nfc_tag_unsupported">Tag does not support NDEF!</string>
<string name="nfc_tag_size">Tag provides %d bytes, but we need %d!</string>
<string name="nfc_tag_read_undef">I don\'t understand the Tag!</string>
<string name="nfc_tag_read_what">I don\'t know what you want!</string>
<string name="nfc_tag_read_success">Reading Tag successful</string>
<string name="nfc_tag_tap">NFC Available!</string>
</resources>

View File

@ -384,4 +384,14 @@
<string name="toast_ledger_attached">%1$s attached</string>
<string name="toast_ledger_detached">%1$s detached</string>
<string name="progress_nfc_write">Writing Tag</string>
<string name="nfc_write_failed">Writing Tag failed!</string>
<string name="nfc_write_successful">Writing Tag successful</string>
<string name="nfc_tag_unsupported">Tag does not support NDEF!</string>
<string name="nfc_tag_size">Tag provides %d bytes, but we need %d!</string>
<string name="nfc_tag_read_undef">I don\'t understand the Tag!</string>
<string name="nfc_tag_read_what">I don\'t know what you want!</string>
<string name="nfc_tag_read_success">Reading Tag successful</string>
<string name="nfc_tag_tap">NFC Available!</string>
</resources>