├── .gitignore ├── .idea ├── .gitignore ├── .name ├── compiler.xml ├── gradle.xml ├── misc.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── de │ │ └── androidcrypto │ │ └── android_emv_nfc_paycard_example │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── de │ │ │ └── androidcrypto │ │ │ └── android_emv_nfc_paycard_example │ │ │ ├── MainActivity.java │ │ │ └── PcscProvider.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values-night │ │ └── themes.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── de │ └── androidcrypto │ └── android_emv_nfc_paycard_example │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Android-EMV-NFC-Paycard-Example -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android EMV NFC Paycard Example 2 | 3 | This sample program shows how to implement the library EMV-NFC-Paycard-Enrollment 4 | for reading the data on an EMC (CreditCard etc) with the NFC technology (contactless). 5 | 6 | Source code: https://github.com/devnied/EMV-NFC-Paycard-Enrollment 7 | 8 | The app uses the IsoDep class for communication with the NFC card and 9 | the enableReaderMode on the NfcAdapter for detecting a NFC tag; 10 | this mode is more reliable then the often used enableForegroundDispatch. 11 | see here for a more detailed explanation: 12 | 13 | https://stackoverflow.com/questions/33633736/whats-the-difference-between-enablereadermode-and-enableforegrounddispatch 14 | 15 | This app is getting just a small subset of all available data fields on an EMV card: 16 | typeName, aid(s), card number and expiration date of the card. 17 | The complete code is available here: 18 | 19 | https://github.com/AndroidCrypto/Android-EMV-NFC-Paycard-Example 20 | 21 | Don't forget to view the Logcat as the app gives a deep look to the commands and data 22 | that is exchanged between the Android device and the EMV card 23 | 24 | As this app does not use any intent filter the data grabbing will work only if the app is 25 | in the foreground when the EMV card is detected. 26 | 27 | The app was tested on a real device with Android 8 (SDK 26) and Android 12 (SDK 31). 28 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | compileSdk 32 7 | 8 | defaultConfig { 9 | applicationId "de.androidcrypto.android_emv_nfc_paycard_example" 10 | minSdk 26 11 | targetSdk 32 12 | versionCode 1 13 | versionName "1.0" 14 | 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | } 17 | 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | compileOptions { 25 | sourceCompatibility JavaVersion.VERSION_1_8 26 | targetCompatibility JavaVersion.VERSION_1_8 27 | } 28 | } 29 | 30 | dependencies { 31 | 32 | implementation 'androidx.appcompat:appcompat:1.4.1' 33 | implementation 'com.google.android.material:material:1.6.0' 34 | implementation 'androidx.constraintlayout:constraintlayout:2.1.3' 35 | testImplementation 'junit:junit:4.13.2' 36 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 37 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 38 | implementation 'com.github.devnied.emvnfccard:library:3.0.1' 39 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/de/androidcrypto/android_emv_nfc_paycard_example/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package de.androidcrypto.android_emv_nfc_paycard_example; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("de.androidcrypto.android_emv_nfc_paycard_example", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/java/de/androidcrypto/android_emv_nfc_paycard_example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package de.androidcrypto.android_emv_nfc_paycard_example; 2 | 3 | import android.nfc.NfcAdapter; 4 | import android.nfc.Tag; 5 | import android.nfc.tech.IsoDep; 6 | import android.os.Bundle; 7 | import android.os.VibrationEffect; 8 | import android.os.Vibrator; 9 | import android.widget.TextView; 10 | 11 | import androidx.appcompat.app.AppCompatActivity; 12 | 13 | import com.github.devnied.emvnfccard.enums.EmvCardScheme; 14 | import com.github.devnied.emvnfccard.model.Application; 15 | import com.github.devnied.emvnfccard.model.EmvCard; 16 | import com.github.devnied.emvnfccard.parser.EmvTemplate; 17 | 18 | import java.io.IOException; 19 | import java.time.LocalDate; 20 | import java.time.ZoneId; 21 | import java.util.Date; 22 | import java.util.List; 23 | 24 | public class MainActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback { 25 | /** 26 | * This sample program shows how to implement the library EMV-NFC-Paycard-Enrollment 27 | * for reading the data on an EMC (CreditCard etc) with the NFC technology (contactless). 28 | * Source code: https://github.com/devnied/EMV-NFC-Paycard-Enrollment 29 | * 30 | * The app uses the IsoDep class for communication with the NFC card and 31 | * the enableReaderMode on the NfcAdapter for detecting a NFC tag; 32 | * this mode is more reliable then the often used enableForegroundDispatch. 33 | * see here for a more detailed explanation: 34 | * https://stackoverflow.com/questions/33633736/whats-the-difference-between-enablereadermode-and-enableforegrounddispatch 35 | * 36 | * This app is getting just a small subset of all available data fields on an EMV card: 37 | * typeName, aid(s), card number and expiration date of the card. 38 | * The complete code is available here: 39 | * https://github.com/AndroidCrypto/Android-EMV-NFC-Paycard-Example 40 | * 41 | * Don't forget to view the Logcat as the app gives a deep look to the commands and data 42 | * that is exchanged between the Android device and the EMV card 43 | * 44 | * As this app does not use any intent filter the data grabbing will work only if the app is 45 | * in the foreground when the EMV card is detected. 46 | * 47 | * The app was tested on a real device with Android 8 (SDK 26) and Android 12 (SDK 31). 48 | */ 49 | 50 | TextView nfcaContent; 51 | private NfcAdapter mNfcAdapter; 52 | 53 | @Override 54 | protected void onCreate(Bundle savedInstanceState) { 55 | super.onCreate(savedInstanceState); 56 | setContentView(R.layout.activity_main); 57 | nfcaContent = findViewById(R.id.tvNfcaContent); 58 | mNfcAdapter = NfcAdapter.getDefaultAdapter(this); 59 | } 60 | 61 | @Override 62 | protected void onResume() { 63 | super.onResume(); 64 | 65 | if (mNfcAdapter != null) { 66 | Bundle options = new Bundle(); 67 | // Work around for some broken Nfc firmware implementations that poll the card too fast 68 | options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250); 69 | 70 | // Enable ReaderMode for all types of card and disable platform sounds 71 | // the option NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK is NOT set 72 | // to get the data of the tag after reading 73 | mNfcAdapter.enableReaderMode(this, 74 | this, 75 | NfcAdapter.FLAG_READER_NFC_A | 76 | NfcAdapter.FLAG_READER_NFC_B | 77 | NfcAdapter.FLAG_READER_NFC_F | 78 | NfcAdapter.FLAG_READER_NFC_V | 79 | NfcAdapter.FLAG_READER_NFC_BARCODE | 80 | NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS, 81 | options); 82 | } 83 | } 84 | 85 | @Override 86 | protected void onPause() { 87 | super.onPause(); 88 | if (mNfcAdapter != null) 89 | mNfcAdapter.disableReaderMode(this); 90 | } 91 | 92 | // This method is run in another thread when a card is discovered 93 | // !!!! This method cannot cannot direct interact with the UI Thread 94 | // Use `runOnUiThread` method to change the UI from this method 95 | @Override 96 | public void onTagDiscovered(Tag tag) { 97 | 98 | IsoDep isoDep = null; 99 | 100 | // Whole process is put into a big try-catch trying to catch the transceive's IOException 101 | try { 102 | isoDep = IsoDep.get(tag); 103 | if (isoDep != null) { 104 | ((Vibrator) getSystemService(VIBRATOR_SERVICE)).vibrate(VibrationEffect.createOneShot(150, 10)); 105 | } 106 | runOnUiThread(new Runnable() { 107 | @Override 108 | public void run() { 109 | //UI related things, not important for NFC 110 | nfcaContent.setText(""); 111 | } 112 | }); 113 | isoDep.connect(); 114 | byte[] response; 115 | String idContentString = "Content of ISO-DEP tag"; 116 | 117 | PcscProvider provider = new PcscProvider(); 118 | provider.setmTagCom(isoDep); 119 | 120 | EmvTemplate.Config config = EmvTemplate.Config() 121 | .setContactLess(true) 122 | .setReadAllAids(true) 123 | .setReadTransactions(true) 124 | .setRemoveDefaultParsers(false) 125 | .setReadAt(true); 126 | 127 | EmvTemplate parser = EmvTemplate.Builder() 128 | .setProvider(provider) 129 | .setConfig(config) 130 | .build(); 131 | 132 | EmvCard card = parser.readEmvCard(); 133 | String cardNumber = card.getCardNumber(); 134 | Date expireDate = card.getExpireDate(); 135 | LocalDate date = LocalDate.of(1999, 12, 31); 136 | if (expireDate != null) { 137 | date = expireDate.toInstant() 138 | .atZone(ZoneId.systemDefault()) 139 | .toLocalDate(); 140 | } 141 | EmvCardScheme cardGetType = card.getType(); 142 | if (cardGetType != null) { 143 | String typeName = card.getType().getName(); 144 | String[] typeAids = card.getType().getAid(); 145 | idContentString = idContentString + "\n" + "typeName: " + typeName; 146 | for (int i = 0; i < typeAids.length; i++) { 147 | idContentString = idContentString + "\n" + "aid " + i + " : " + typeAids[i]; 148 | } 149 | } 150 | 151 | List applications = card.getApplications(); 152 | idContentString = idContentString + "\n" + "cardNumber: " + prettyPrintCardNumber(cardNumber); 153 | idContentString = idContentString + "\n" + "expireDate: " + date; 154 | 155 | String finalIdContentString = idContentString; 156 | runOnUiThread(new Runnable() { 157 | @Override 158 | public void run() { 159 | //UI related things, not important for NFC 160 | nfcaContent.setText(finalIdContentString); 161 | } 162 | }); 163 | try { 164 | isoDep.close(); 165 | } catch (IOException e) { 166 | e.printStackTrace(); 167 | } 168 | } catch (IOException e) { 169 | //Trying to catch any ioexception that may be thrown 170 | e.printStackTrace(); 171 | } catch (Exception e) { 172 | //Trying to catch any exception that may be thrown 173 | e.printStackTrace(); 174 | } 175 | 176 | } 177 | 178 | @Override 179 | public void onPointerCaptureChanged(boolean hasCapture) { 180 | super.onPointerCaptureChanged(hasCapture); 181 | } 182 | 183 | public static String prettyPrintCardNumber(String cardNumber) { 184 | if (cardNumber == null) return null; 185 | char delimiter = ' '; 186 | return cardNumber.replaceAll(".{4}(?!$)", "$0" + delimiter); 187 | } 188 | 189 | public static String bytesToHex(byte[] bytes) { 190 | StringBuffer result = new StringBuffer(); 191 | for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1)); 192 | return result.toString(); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /app/src/main/java/de/androidcrypto/android_emv_nfc_paycard_example/PcscProvider.java: -------------------------------------------------------------------------------- 1 | package de.androidcrypto.android_emv_nfc_paycard_example; 2 | 3 | import android.nfc.tech.IsoDep; 4 | import android.util.Log; 5 | 6 | import com.github.devnied.emvnfccard.enums.SwEnum; 7 | import com.github.devnied.emvnfccard.exception.CommunicationException; 8 | import com.github.devnied.emvnfccard.parser.IProvider; 9 | import com.github.devnied.emvnfccard.utils.TlvUtil; 10 | 11 | import java.io.IOException; 12 | 13 | import fr.devnied.bitlib.BytesUtils; 14 | 15 | public class PcscProvider implements IProvider { 16 | private static final String TAG = "Provider"; 17 | private IsoDep mTagCom; 18 | 19 | public void setmTagCom(final IsoDep mTagCom) { 20 | this.mTagCom = mTagCom; 21 | } 22 | 23 | @Override 24 | public byte[] transceive(byte[] pCommand) throws CommunicationException { 25 | byte[] response = null; 26 | try { 27 | // send command to emv card 28 | mTagCom.getTag(); 29 | //mTagCom.connect(); 30 | if (mTagCom.isConnected()){ 31 | response = mTagCom.transceive(pCommand); 32 | } 33 | } catch (IOException e) { 34 | throw new CommunicationException(e.getMessage()); 35 | } 36 | Log.d(TAG, "resp: " + BytesUtils.bytesToString(response)); 37 | try { 38 | Log.d(TAG, "resp: " + TlvUtil.prettyPrintAPDUResponse(response)); 39 | SwEnum val = SwEnum.getSW(response); 40 | if (val != null) { 41 | Log.d(TAG, "resp: " + val.getDetail()); 42 | } 43 | } catch (Exception e) { 44 | } 45 | return response; 46 | } 47 | 48 | @Override 49 | public byte[] getAt() { 50 | // return new byte[0]; // from Stackoverflow 51 | byte[] result; 52 | result = mTagCom.getHistoricalBytes(); // for tags using NFC-B 53 | if (result == null) { 54 | result = mTagCom.getHiLayerResponse(); // for tags using NFC-B 55 | } 56 | return result; 57 | } 58 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 23 | 24 | 33 | 34 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidCrypto/Android-EMV-NFC-Paycard-Example/6e70f91e18d8d7a3b9a1ee0f8ad502f4d72a3009/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidCrypto/Android-EMV-NFC-Paycard-Example/6e70f91e18d8d7a3b9a1ee0f8ad502f4d72a3009/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidCrypto/Android-EMV-NFC-Paycard-Example/6e70f91e18d8d7a3b9a1ee0f8ad502f4d72a3009/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidCrypto/Android-EMV-NFC-Paycard-Example/6e70f91e18d8d7a3b9a1ee0f8ad502f4d72a3009/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidCrypto/Android-EMV-NFC-Paycard-Example/6e70f91e18d8d7a3b9a1ee0f8ad502f4d72a3009/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidCrypto/Android-EMV-NFC-Paycard-Example/6e70f91e18d8d7a3b9a1ee0f8ad502f4d72a3009/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidCrypto/Android-EMV-NFC-Paycard-Example/6e70f91e18d8d7a3b9a1ee0f8ad502f4d72a3009/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidCrypto/Android-EMV-NFC-Paycard-Example/6e70f91e18d8d7a3b9a1ee0f8ad502f4d72a3009/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidCrypto/Android-EMV-NFC-Paycard-Example/6e70f91e18d8d7a3b9a1ee0f8ad502f4d72a3009/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidCrypto/Android-EMV-NFC-Paycard-Example/6e70f91e18d8d7a3b9a1ee0f8ad502f4d72a3009/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Android-EMV-NFC-Paycard-Example 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /app/src/test/java/de/androidcrypto/android_emv_nfc_paycard_example/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package de.androidcrypto.android_emv_nfc_paycard_example; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | id 'com.android.application' version '7.2.0' apply false 4 | id 'com.android.library' version '7.2.0' apply false 5 | } 6 | 7 | task clean(type: Delete) { 8 | delete rootProject.buildDir 9 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Enables namespacing of each library's R class so that its R class includes only the 19 | # resources declared in the library itself and none from the library's dependencies, 20 | # thereby reducing the size of the R class for that library 21 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidCrypto/Android-EMV-NFC-Paycard-Example/6e70f91e18d8d7a3b9a1ee0f8ad502f4d72a3009/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed May 18 21:01:52 CEST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | } 15 | rootProject.name = "Android-EMV-NFC-Paycard-Example" 16 | include ':app' 17 | --------------------------------------------------------------------------------