Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android: set up NFC app linking #1003

Merged
merged 5 commits into from
May 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ dependencies {
// gif
implementation 'com.facebook.fresco:fresco:2.6.0'
implementation 'com.facebook.fresco:animated-gif:2.6.0'
implementation "com.hypertrack:hyperlog:0.0.10"

if (enableHermes) {
def hermesPath = "../../node_modules/hermes-engine/android/";
Expand Down
2 changes: 0 additions & 2 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize"
android:screenOrientation="portrait">
<meta-data android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="@xml/nfc_tech_filter" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Expand Down
1 change: 1 addition & 0 deletions android/app/src/main/java/com/zeus/MainApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public boolean getUseDeveloperSupport() {
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
packages.add(new MobileToolsPackage());
return packages;
}
@Override
Expand Down
90 changes: 90 additions & 0 deletions android/app/src/main/java/com/zeus/MobileTools.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// based on Blixt LNDMobileTools
// https://github.com/hsjoberg/blixt-wallet/blob/master/android/app/src/main/java/com/blixtwallet/MainApplication.java
package app.zeusln.zeus;

import android.nfc.Tag;
import android.nfc.NfcAdapter;
import android.nfc.NdefMessage;
import android.nfc.tech.Ndef;
import android.nfc.NdefRecord;

import java.util.Arrays;
import java.io.UnsupportedEncodingException;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Promise;

import com.hypertrack.hyperlog.HyperLog;

class MobileTools extends ReactContextBaseJavaModule {
final String TAG = "MobileTools";

public MobileTools(ReactApplicationContext reactContext) {
super(reactContext);
}

@Override
public String getName() {
return "MobileTools";
}

@ReactMethod
public void getIntentNfcData(Promise promise) {
// https://code.tutsplus.com/tutorials/reading-nfc-tags-with-android--mobile-17278
Tag tag = getReactApplicationContext()
.getCurrentActivity().getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (tag == null) {
promise.resolve(null);
return;
}

Ndef ndef = Ndef.get(tag);
if (ndef == null) {
HyperLog.d(TAG, "NFC tag is not NDEF");
promise.resolve(null);
}

NdefMessage ndefMessage = ndef.getCachedNdefMessage();

NdefRecord[] records = ndefMessage.getRecords();
if (records.length > 0) {
// Get first record and ignore the rest
NdefRecord record = records[0];
if (record.getTnf() == NdefRecord.TNF_WELL_KNOWN && Arrays.equals(record.getType(), NdefRecord.RTD_TEXT)) {
/*
* See NFC forum specification for "Text Record Type Definition" at 3.2.1
*
* http://www.nfc-forum.org/specs/
*
* bit_7 defines encoding
* bit_6 reserved for future use, must be 0
* bit_5..0 length of IANA language code
*/
byte[] payload = record.getPayload();

// Get the Text Encoding
String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16";

// Get the Language Code
int languageCodeLength = payload[0] & 0063;

// String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
// e.g. "en"

try {
String s = new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
promise.resolve(s);
return;
} catch (UnsupportedEncodingException e) {
HyperLog.e(TAG, "Error returning ndef data", e);
}
}
else {
HyperLog.d(TAG, "Cannot read NFC Tag Record");
}
}
promise.resolve(null);
}
}
22 changes: 22 additions & 0 deletions android/app/src/main/java/com/zeus/MobileToolsPackage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package app.zeusln.zeus;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class MobileToolsPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}

@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Arrays.asList(new MobileTools(reactContext));
}
}
8 changes: 0 additions & 8 deletions android/app/src/main/res/xml/nfc_tech_filter.xml

This file was deleted.

12 changes: 9 additions & 3 deletions stores/InvoicesStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,14 +221,20 @@ export default class InvoicesStore {
@action
public clearAddress = () => (this.onChainAddress = null);

@action
public clearPayReq = () => {
this.pay_req = null;
this.getPayReqError = null;
};

@action
public getPayReq = (
paymentRequest: string,
descriptionPreimage?: string
) => {
this.loading = true;
this.pay_req = null;
this.paymentRequest = paymentRequest;
this.loading = true;
this.feeEstimate = null;

return RESTUtils.decodePaymentRequest([paymentRequest])
Expand All @@ -249,14 +255,14 @@ export default class InvoicesStore {
);
}

this.loading = false;
this.getPayReqError = null;
this.loading = false;
})
.catch((error: any) => {
// handle error
this.loading = false;
this.pay_req = null;
this.getPayReqError = error.toString();
this.loading = false;
});
};

Expand Down
16 changes: 12 additions & 4 deletions utils/LinkingUtils.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { Linking } from 'react-native';
import { Linking, NativeModules } from 'react-native';
import { localeString } from './LocaleUtils';
import handleAnything from './handleAnything';

class LinkingUtils {
handleInitialUrl = (navigation: any) =>
Linking.getInitialURL().then(
(url) => url && this.handleDeepLink(url, navigation)
);
Linking.getInitialURL().then(async (url) => {
if (url) {
this.handleDeepLink(url, navigation);
return;
}
if (Platform.OS === 'android') {
const nfcData =
await NativeModules.MobileTools.getIntentNfcData();
if (nfcData) this.handleDeepLink(nfcData, navigation);
}
});

handleDeepLink = (url: string, navigation: any) =>
handleAnything(url)
Expand Down
10 changes: 7 additions & 3 deletions views/PaymentRequest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ export default class PaymentRequest extends React.Component<
loading,
loadingFeeEstimate,
successProbability,
feeEstimate
feeEstimate,
clearPayReq
} = InvoicesStore;

const requestAmount = pay_req && pay_req.getRequestAmount;
Expand Down Expand Up @@ -133,7 +134,10 @@ export default class PaymentRequest extends React.Component<
const BackButton = () => (
<Icon
name="arrow-back"
onPress={() => navigation.navigate('Wallet', { refresh: true })}
onPress={() => {
clearPayReq();
navigation.navigate('Wallet', { refresh: true });
}}
color={themeColor('text')}
underlayColor="transparent"
/>
Expand Down Expand Up @@ -178,7 +182,7 @@ export default class PaymentRequest extends React.Component<
</View>
)}

{!!pay_req && (
{!loading && !!pay_req && (
<View style={styles.content}>
<View style={styles.center}>
{isNoAmountInvoice ? (
Expand Down