diff --git a/.idea/misc.xml b/.idea/misc.xml index c0f68ed..99202cc 100755 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,34 +1,34 @@ - + \ No newline at end of file diff --git a/app/src/main/java/org/dslul/ticketreader/MainActivity.java b/app/src/main/java/org/dslul/ticketreader/MainActivity.java index 120f24b..34b9695 100755 --- a/app/src/main/java/org/dslul/ticketreader/MainActivity.java +++ b/app/src/main/java/org/dslul/ticketreader/MainActivity.java @@ -1,394 +1,394 @@ package org.dslul.ticketreader; import android.annotation.SuppressLint; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.nfc.tech.IsoDep; import android.os.Bundle; import android.os.CountDownTimer; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.CardView; import android.support.v7.widget.Toolbar; import android.text.Html; import android.text.method.LinkMovementMethod; import android.util.Log; import android.view.View; import android.view.Menu; import android.view.MenuItem; import android.widget.ImageView; import android.widget.TableLayout; import android.widget.TextView; import java.util.Calendar; import java.util.List; import java.util.concurrent.TimeUnit; import android.nfc.NfcAdapter; import android.nfc.tech.NfcA; import android.widget.Toast; import android.content.Intent; import android.content.IntentFilter; import android.app.PendingIntent; import android.os.Handler; import android.os.Message; import android.app.AlertDialog; import android.content.DialogInterface; import com.google.android.gms.ads.AdRequest; import com.google.android.gms.ads.AdView; import com.google.android.gms.ads.MobileAds; import com.yarolegovich.lovelydialog.LovelyCustomDialog; import com.yarolegovich.lovelydialog.LovelyInfoDialog; import com.yarolegovich.lovelydialog.LovelyStandardDialog; import org.dslul.ticketreader.util.HelperFunctions; public class MainActivity extends AppCompatActivity { private NfcAdapter mNfcAdapter; private IntentFilter tech; private IntentFilter[] intentFiltersArray; private PendingIntent pendingIntent; private Intent intent; private AlertDialog alertDialog; private Toast currentToast; private AdView adview; private ImageView imageNfc; private CardView ticketCard; private CardView statusCard; private ImageView statusImg; private TextView statoBiglietto; private TextView infoLabel; private TableLayout infoTable; private TextView tipologia; private TextView dataLabel; private TextView dataObliterazione; private TextView corseRimanenti; private CountDownTimer timer; private List dump; // list of NFC technologies detected: private final String[][] techListsArray = new String[][] { new String[] { //MifareUltralight.class.getName(), NfcA.class.getName() }, new String[] { IsoDep.class.getName() } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); adview = (AdView) findViewById(R.id.adView); imageNfc = (ImageView) findViewById(R.id.imagenfcView); ticketCard = (CardView) findViewById(R.id.ticketCardView); statusCard = (CardView) findViewById(R.id.statusCardView); statusImg = (ImageView) findViewById(R.id.statusImg); statoBiglietto = (TextView) findViewById(R.id.stato_biglietto); infoLabel = (TextView) findViewById(R.id.infolabel); infoTable = (TableLayout) findViewById(R.id.info_table); tipologia = (TextView) findViewById(R.id.tipologia); dataLabel = (TextView) findViewById(R.id.validation_or_expire); dataObliterazione = (TextView) findViewById(R.id.data_obliterazione); corseRimanenti = (TextView) findViewById(R.id.corse_rimaste); MobileAds.initialize(this, "ca-app-pub-2102716674867426~1964394961"); AdRequest adRequest = new AdRequest.Builder().build(); adview.loadAd(adRequest); mNfcAdapter = NfcAdapter.getDefaultAdapter(this); if (mNfcAdapter == null) { Toast.makeText(this, R.string.nfc_not_supported, Toast.LENGTH_LONG).show(); finish(); return; } if (!mNfcAdapter.isEnabled()) { Toast.makeText(this, R.string.nfc_disabled, Toast.LENGTH_LONG).show(); startActivity(new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS)); } tech = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED); intentFiltersArray = new IntentFilter[] {tech}; intent = new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); //FLAG_ACTIVITY_REORDER_TO_FRONT FLAG_RECEIVER_REPLACE_PENDING pendingIntent = PendingIntent.getActivity(this, 0, intent, 0); onNewIntent(getIntent()); } @Override protected void onResume() { super.onResume(); mNfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, this.techListsArray); } @Override protected void onPause() { // disabling foreground dispatch: //NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); mNfcAdapter.disableForegroundDispatch(this); super.onPause(); } @Override protected void onNewIntent(Intent intent) { if (intent.getAction().equals(NfcAdapter.ACTION_TECH_DISCOVERED)) { NfcThread nfcThread = new NfcThread(getBaseContext(), intent, mContentHandler, mToastShortHandler, mToastLongHandler, mShowInfoDialogHandler); nfcThread.start(); } } @SuppressLint("HandlerLeak") private Handler mContentHandler = new Handler() { public void handleMessage(Message msg) { List dumplist = (List)msg.obj; dump = dumplist; if(timer != null) timer.cancel(); try { //smartcard if(dumplist.size() == 15) { - SmartCard smartcard = new SmartCard(dumplist, getBaseContext()); + SmartCard smartcard = new SmartCard(dumplist); if(smartcard.isSubscription()) { dataLabel.setText(R.string.expire_date); tipologia.setText(smartcard.getSubscriptionName()); dataObliterazione.setText(smartcard.getDate()); if(smartcard.isExpired()) { corseRimanenti.setText("0"); statoBiglietto.setText(R.string.expired); statusImg.setImageResource(R.drawable.ic_error_grey_800_36dp); statusCard.setCardBackgroundColor(getResources().getColor(R.color.colorRed)); } else { corseRimanenti.setText(R.string.unlimited); statoBiglietto.setText(R.string.valid); statusImg.setImageResource(R.drawable.ic_check_circle_grey_800_36dp); statusCard.setCardBackgroundColor(getResources().getColor(R.color.colorGreen)); } statusCard.setVisibility(View.VISIBLE); ticketCard.setVisibility(View.VISIBLE); infoLabel.setText(R.string.read_another_ticket); imageNfc.setVisibility(View.GONE); } else { createTicketInterface(smartcard.getName(),smartcard.getValidationDate(), smartcard.getRemainingRides(), smartcard.getRemainingMinutes()); //Toast.makeText(getBaseContext(), R.string.smartcard_tickets_not_supported_yet, Toast.LENGTH_LONG).show(); } } //chip on paper else if(dumplist.size() > 15) { ChipOnPaper chipOnPaper = new ChipOnPaper(dumplist); createTicketInterface(chipOnPaper.getTypeName(),chipOnPaper.getDate(), chipOnPaper.getRemainingRides(), chipOnPaper.getRemainingMinutes()); } else { statusCard.setVisibility(View.GONE); ticketCard.setVisibility(View.GONE); infoLabel.setText(R.string.info_instructions); imageNfc.setVisibility(View.VISIBLE); } } catch (Exception ex) { Toast.makeText(getBaseContext(), R.string.unknown_error, Toast.LENGTH_LONG).show(); Log.d("card", ex.getMessage()); } } }; private void createTicketInterface(String name, String date, int remainingRides, long remainingMinutes) { dataLabel.setText(R.string.data_obliterazione); tipologia.setText(name); dataObliterazione.setText(date); corseRimanenti.setText(Integer.toString(remainingRides)); if(remainingMinutes != 0) { statoBiglietto.setText(R.string.in_corso); statusImg.setImageResource(R.drawable.ic_restore_grey_800_36dp); statusCard.setCardBackgroundColor(getResources().getColor(R.color.colorBlue)); Calendar calendar = Calendar.getInstance(); int sec = calendar.get(Calendar.SECOND); timer = new CountDownTimer((remainingMinutes*60 - sec)*1000, 1000) { public void onTick(long millis) { statoBiglietto.setText(String.format(getResources().getString(R.string.in_corso), TimeUnit.MILLISECONDS.toMinutes(millis), TimeUnit.MILLISECONDS.toSeconds(millis) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)))); } public void onFinish() { statoBiglietto.setText(R.string.corse_esaurite); statusImg.setImageResource(R.drawable.ic_error_grey_800_36dp); statusCard.setCardBackgroundColor(getResources().getColor(R.color.colorRed)); if(timer != null) timer.cancel(); } }.start(); } else if(remainingRides == 0 && remainingMinutes == 0) { statoBiglietto.setText(R.string.corse_esaurite); statusImg.setImageResource(R.drawable.ic_error_grey_800_36dp); statusCard.setCardBackgroundColor(getResources().getColor(R.color.colorRed)); } else if(remainingRides != 0 && remainingMinutes == 0) { statoBiglietto.setText(String.format(getResources().getString(R.string.corse_disponibili), remainingRides)); statusImg.setImageResource(R.drawable.ic_check_circle_grey_800_36dp); statusCard.setCardBackgroundColor(getResources().getColor(R.color.colorGreen)); } statusCard.setVisibility(View.VISIBLE); ticketCard.setVisibility(View.VISIBLE); infoLabel.setText(R.string.read_another_ticket); imageNfc.setVisibility(View.GONE); } private Handler mToastShortHandler = new Handler() { public void handleMessage(Message msg) { String text = (String)msg.obj; if(currentToast != null) currentToast.cancel(); currentToast = Toast.makeText(MainActivity.this, text, Toast.LENGTH_SHORT); currentToast.show(); } }; private Handler mToastLongHandler = new Handler() { public void handleMessage(Message msg) { String text = (String)msg.obj; if(currentToast != null) currentToast.cancel(); currentToast = Toast.makeText(MainActivity.this, text, Toast.LENGTH_LONG); currentToast.show(); } }; private Handler mShowInfoDialogHandler = new Handler() { public void handleMessage(Message msg) { String text = (String)msg.obj; //infoDialog = showInfoDialog(text); //infoDialog.show(); } }; @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_info) { TextView view = new TextView(getBaseContext()); view.setText(Html.fromHtml(getString(R.string.html_info))); view.setMovementMethod(LinkMovementMethod.getInstance()); view.setPadding( 40, 40, 40, 40 ); new LovelyCustomDialog(this) .setTopColorRes(R.color.darkBlueGrey) .setIcon(R.drawable.ic_info_outline_white_36dp) .setTitle("Info") .setView(view) .show(); return true; } if (id == R.id.action_copy_content) { try { ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); String content = ""; if (dump != null && dump.size() >= 15) { for (byte[] page : dump) { content = content.concat(HelperFunctions.byteArrayToHexString(page)); } ClipData clip = ClipData.newPlainText("content", content); if (clipboard != null) { clipboard.setPrimaryClip(clip); } Toast.makeText(getBaseContext(), R.string.content_copied, Toast.LENGTH_LONG).show(); } else { Toast.makeText(getBaseContext(), R.string.no_content, Toast.LENGTH_LONG).show(); } } catch (Exception ex) { ex.printStackTrace(); } return true; } return super.onOptionsItemSelected(item); } private AlertDialog showAlertDialog(String message) { DialogInterface.OnClickListener dialogInterfaceListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { alertDialog.cancel(); } }; alertDialog = new AlertDialog.Builder(this) .setTitle(R.string.information) .setIcon(android.R.drawable.ic_dialog_info) .setMessage(message) .setPositiveButton(R.string.close_dialog, null) .create(); alertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { public void onCancel(DialogInterface dialog) { } }); return alertDialog; } } diff --git a/app/src/main/java/org/dslul/ticketreader/SmartCard.java b/app/src/main/java/org/dslul/ticketreader/SmartCard.java index e014d7f..dc6e0b9 100644 --- a/app/src/main/java/org/dslul/ticketreader/SmartCard.java +++ b/app/src/main/java/org/dslul/ticketreader/SmartCard.java @@ -1,312 +1,322 @@ package org.dslul.ticketreader; -import android.content.Context; - import org.dslul.ticketreader.util.GttDate; import java.text.DateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import static java.lang.Math.abs; +import static org.dslul.ticketreader.util.HelperFunctions.byteArrayToHexString; import static org.dslul.ticketreader.util.HelperFunctions.getBytesFromPage; public class SmartCard { public enum Type { BIP, PYOU, EDISU } static final Map subscriptionCodes = new HashMap() {{ put(68, "Mensile UNDER 26"); put(72, "Mensile Studenti Rete Urbana"); put(101, "Settimanale Formula 1"); put(102, "Settimanale Formula 2"); put(103, "Settimanale Formula 3"); put(104, "Settimanale Formula 4"); put(105, "Settimanale Formula 5"); put(106, "Settimanale Formula 6"); put(107, "Settimanale Formula 7"); put(108, "Settimanale Intera Area Formula"); put(109, "Settimanale Personale Rete Urbana"); put(199, "Mensile Personale Rete Urbana (Formula U)"); put(201, "Mensile Formula 1"); put(202, "Mensile Formula 2"); put(203, "Mensile Formula 3"); put(204, "Mensile Formula 4"); put(205, "Mensile Formula 5"); put(206, "Mensile Formula 6"); put(207, "Mensile Formula 7"); put(208, "Mensile Intera Area Formula"); put(261, "Mensile Studenti Urbano+Suburbano"); put(290, "Mensile 65+ Urbano Orario Ridotto"); put(291, "Mensile 65+ Urbano"); put(307, "Annuale Ivrea Rete Urbana e Dintorni"); put(308, "Annuale Extraurbano O/D"); put(310, "Plurimensile Studenti Extraurbano O/D"); put(721, "Annuale UNDER 26"); put(722, "Annuale UNDER 26 Fascia A"); put(723, "Annuale UNDER 26 Fascia B"); put(724, "Annuale UNDER 26 Fascia C"); put(730, "Mensile urbano Over 65"); put(761, "Annuale Over A"); put(731, "Annuale Over B"); put(732, "Annuale Over C"); put(733, "Annuale Over D"); put(911, "10 Mesi Studenti"); put(912, "Annuale Studenti"); put(990, "Junior"); put(993, "Annuale Formula U"); put(4003, "Annuale Formula U a Zone"); }}; static final Map ticketCodes = new HashMap() {{ put(712, "Ordinario Urbano"); put(714, "City 100"); put(715, "Daily"); put(716, "Multidaily"); }}; - Context context; - private class Contract { private int code; private boolean isValid; private boolean isTicket; private boolean isSubscription; private Date startDate; private Date endDate; public Contract(byte[] data) { //get contract type code = ((data[4] & 0xff) << 8) | data[5] & 0xff; if(code == 0) { isValid = false; } else { isValid = true; } if(ticketCodes.containsKey(code)) { isTicket = true; isSubscription = false; } else if(subscriptionCodes.containsKey(code)) { isTicket = false; isSubscription = true; } else { isTicket = false; isSubscription = false; } long minutes = ~(data[9] << 16 & 0xff0000 | data[10] << 8 & 0xff00 | data[11] & 0xff) & 0xffffff; startDate = GttDate.decode(minutes); minutes = ~(data[12] << 16 & 0xff0000 | data[13] << 8 & 0xff00 | data[14] & 0xff) & 0xffffff; endDate = GttDate.decode(minutes); } public int getCode() { return code; } public Date getStartDate() { return startDate; } public Date getEndDate() { return endDate; } public boolean isContract() { return isValid; } public boolean isSubscription() { if(isSubscription) return true; else return false; } public boolean isTicket() { if(isTicket) return true; else return false; } public String getTypeName() { if(isTicket) return ticketCodes.get(code); else if(isSubscription) return subscriptionCodes.get(code); else return "Sconosciuto"; } } private byte[] efEnvironment; private Date validationDate; private Date creationDate; private Type type; private List contracts = new ArrayList<>(); private boolean isSubscription = false; private Contract latestContract; private String subscriptionName; private int ridesLeft = 0; private long remainingMins; - SmartCard(List dumplist, Context context) { - this.context = context; - + SmartCard(List dumplist) { efEnvironment = dumplist.get(1); byte[] efContractList = dumplist.get(2); + byte[] efEventLogs1 = dumplist.get(11); + byte[] efEventLogs2 = dumplist.get(12); + byte[] efEventLogs3 = dumplist.get(13); byte[] minutes = new byte[3]; System.arraycopy(efEnvironment, 9, minutes, 0, 3); creationDate = GttDate.decode(minutes); if(efEnvironment[28] == (byte)0xC0) type = Type.BIP; else if(efEnvironment[28] == (byte)0xC1) type = Type.PYOU; else if(efEnvironment[28] == (byte)0xC2) type = Type.EDISU; Date latestExpireDate = GttDate.getGttEpoch(); for (int i = 0; i < 8; i++) { Contract contract = new Contract(dumplist.get(i+3)); if(contract.isContract()) { if(latestExpireDate.before(contract.getEndDate())) { latestExpireDate = contract.getEndDate(); latestContract = contract; } if(contract.isSubscription()) { isSubscription = true; subscriptionName = contract.getTypeName(); if(!isExpired(contract.getEndDate())) break; } if(contract.isTicket()) { //ridesLeft += 1; if(isExpired(latestContract.getEndDate())) isSubscription = false; //TODO: count tickets in daily 7 carnets } //isSubscription = contract.isSubscription(); contracts.add(contract); } } //actual tickets count - for (int i = 2; i < 29; i+=3) { - int num = abs(efContractList[i+1]) >> 4; - int used = efContractList[i+2]; - if(num != 0 && num <= 8 && used == 0) - ridesLeft += 1; - } + ridesLeft = countTickets(efContractList, efEventLogs1, efEventLogs2, efEventLogs3); //get last validation time - long mins = getBytesFromPage(dumplist.get(11), 20, 3); + long mins = getBytesFromPage(efEventLogs1, 20, 3); validationDate = GttDate.addMinutesToDate(mins, GttDate.getGttEpoch()); Calendar c = Calendar.getInstance(); long diff = (c.getTime().getTime() - validationDate.getTime()) / 60000; - int num = (int)(getBytesFromPage(dumplist.get(11), 25, 1) >> 4); + int num = (int)(getBytesFromPage(efEventLogs1, 25, 1) >> 4); int tickettype = (int)getBytesFromPage(dumplist.get(num+2), 4, 2); long maxtime = 90; //city 100 if(tickettype == 714) { maxtime = 100; } //daily if(tickettype == 715 || tickettype == 716) { maxtime = GttDate.getMinutesUntilMidnight(); } if(diff >= maxtime) { remainingMins = 0; } else { remainingMins = maxtime - diff; } } + private int countTickets(byte[] contractsList, byte[] evLogs1, byte[] evLogs2, byte[] evLogs3) { + int count = 0; + for (int i = 2; i < 24; i+=3) { + int num = abs(contractsList[i+1]) >> 4; + //int used = contractsList[i+2]; + if(num != 0 && num <= 8 && (contractsList[i]&0x0f) != 0) { + int lognum1 = abs(evLogs1[25]) >> 4; + int lognum2 = abs(evLogs2[25]) >> 4; + int lognum3 = abs(evLogs3[25]) >> 4; + //TODO: find out how to count multi daily 7 tickets + if(num != lognum1 && num != lognum2 && num != lognum3) + count += 1; + } + } + return count; + } + public String getName() { return type + " - " + latestContract.getTypeName(); } public String getSubscriptionName() { return type + " - " + subscriptionName; } public String getDate() { return DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT) .format(latestContract.getEndDate()); } public String getValidationDate() { return DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT) .format(validationDate); } public boolean isExpired() { Calendar c = Calendar.getInstance(); return c.getTime().after(latestContract.getEndDate()); } private boolean isExpired(Date date) { Calendar c = Calendar.getInstance(); return c.getTime().after(date); } public int getRemainingRides() { return ridesLeft; } public long getRemainingMinutes() { return remainingMins; } public boolean isSubscription() { return isSubscription; } } diff --git a/app/src/main/res/drawable/ic_nfc.xml b/app/src/main/res/drawable/ic_nfc.xml index 1db1c22..ac9af18 100755 --- a/app/src/main/res/drawable/ic_nfc.xml +++ b/app/src/main/res/drawable/ic_nfc.xml @@ -1,5 +1,5 @@ - - + + diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml index 144b3f7..465051a 100755 --- a/app/src/main/res/layout/content_main.xml +++ b/app/src/main/res/layout/content_main.xml @@ -1,223 +1,223 @@ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 30ae204..b892268 100755 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,18 +1,20 @@ #00529f #00529f #FF4081 #FFA5D6A7 #FF90CAF9 #FFEF9A9A + #515A66 + #3F51B5 #009688 #E64A19 #388E3C #455A64 #D32F2F #FFFFFF diff --git a/app/src/test/java/org/dslul/ticketreader/ChipOnPaperUnitTest.java b/app/src/test/java/org/dslul/ticketreader/ChipOnPaperUnitTest.java index d1c1e13..ae9d4cd 100755 --- a/app/src/test/java/org/dslul/ticketreader/ChipOnPaperUnitTest.java +++ b/app/src/test/java/org/dslul/ticketreader/ChipOnPaperUnitTest.java @@ -1,50 +1,108 @@ package org.dslul.ticketreader; +import android.test.AndroidTestCase; +import android.test.ApplicationTestCase; +import android.test.InstrumentationTestCase; import android.util.Log; import org.dslul.ticketreader.util.HelperFunctions; import org.junit.Test; import java.util.ArrayList; import java.util.List; +import static org.dslul.ticketreader.util.HelperFunctions.hexStringToByteArray; import static org.junit.Assert.*; /** * Example local unit test, which will execute on the development machine (host). * * @see Testing documentation */ public class ChipOnPaperUnitTest { List ticket2 = new ArrayList<>(); List ticket3 = new ArrayList<>(); List ticket4 = new ArrayList<>(); List ticket5 = new ArrayList<>(); List ticket6 = new ArrayList<>(); @Test public void ChipOnPaper_isCorrect() throws Exception { List ticket1 = new ArrayList<>(); - ticket1.add(HelperFunctions.hexStringToByteArray("057D6292")); - ticket1.add(HelperFunctions.hexStringToByteArray("AD2954E9")); - ticket1.add(HelperFunctions.hexStringToByteArray("3915F203")); - ticket1.add(HelperFunctions.hexStringToByteArray("07FFFFF0")); - ticket1.add(HelperFunctions.hexStringToByteArray("01040000")); - ticket1.add(HelperFunctions.hexStringToByteArray("020102BE")); - ticket1.add(HelperFunctions.hexStringToByteArray("68970000")); - ticket1.add(HelperFunctions.hexStringToByteArray("00AE10A7")); - ticket1.add(HelperFunctions.hexStringToByteArray("0200645C")); - ticket1.add(HelperFunctions.hexStringToByteArray("397D91B4")); - ticket1.add(HelperFunctions.hexStringToByteArray("68A4F900")); - ticket1.add(HelperFunctions.hexStringToByteArray("04F80000")); - ticket1.add(HelperFunctions.hexStringToByteArray("68A4F900")); - ticket1.add(HelperFunctions.hexStringToByteArray("00050004")); - ticket1.add(HelperFunctions.hexStringToByteArray("F8AE1079")); - ticket1.add(HelperFunctions.hexStringToByteArray("9E1291E4")); + ticket1.add(hexStringToByteArray("057D6292")); + ticket1.add(hexStringToByteArray("AD2954E9")); + ticket1.add(hexStringToByteArray("3915F203")); + ticket1.add(hexStringToByteArray("07FFFFF0")); + ticket1.add(hexStringToByteArray("01040000")); + ticket1.add(hexStringToByteArray("020102BE")); + ticket1.add(hexStringToByteArray("68970000")); + ticket1.add(hexStringToByteArray("00AE10A7")); + ticket1.add(hexStringToByteArray("0200645C")); + ticket1.add(hexStringToByteArray("397D91B4")); + ticket1.add(hexStringToByteArray("68A4F900")); + ticket1.add(hexStringToByteArray("04F80000")); + ticket1.add(hexStringToByteArray("68A4F900")); + ticket1.add(hexStringToByteArray("00050004")); + ticket1.add(hexStringToByteArray("F8AE1079")); + ticket1.add(hexStringToByteArray("9E1291E4")); ChipOnPaper chip = new ChipOnPaper(ticket1); assertEquals(4, chip.getRemainingRides()); assertEquals(0, chip.getRemainingMinutes()); } + + @Test + public void Smartcard_Count_Ticket_isCorrect() throws Exception { + List list = new ArrayList<>(); + list.add(hexStringToByteArray("6F208970ABA0B980986C9A09078F098087E0A980DF0101010A0101090E109019011022354345676010019000")); + list.add(hexStringToByteArray("050110129845479323849432659823874899264578987A09A9692348799000")); + + list.add(hexStringToByteArray("05012160014020014030014040014050012110012170012180000000009000")); + + list.add(hexStringToByteArray("00000000000000000000000000000000000000000000000000000000009000")); + list.add(hexStringToByteArray("00000000000000000000000000000000000000000000000000000000009000")); + list.add(hexStringToByteArray("00000000000000000000000000000000000000000000000000000000009000")); + list.add(hexStringToByteArray("00000000000000000000000000000000000000000000000000000000009000")); + list.add(hexStringToByteArray("00000000000000000000000000000000000000000000000000000000009000")); + list.add(hexStringToByteArray("00000000000000000000000000000000000000000000000000000000009000")); + list.add(hexStringToByteArray("00000000000000000000000000000000000000000000000000000000009000")); + list.add(hexStringToByteArray("00000000000000000000000000000000000000000000000000000000009000")); + + list.add(hexStringToByteArray("0501000000216D7F0D0004F800002A00000000006D7F0D00007000BEE39000")); + list.add(hexStringToByteArray("0501000000216D00050004F800000400000000006CFFF00000400007779000")); + list.add(hexStringToByteArray("0501000000216CFFF00004F80000D800000000006CFFF000004000316A9000")); + + list.add(hexStringToByteArray("60000234D95D6F5840000000000060004000004000000000234321E56A821000019000")); + SmartCard smartcard = new SmartCard(list); + + + assertEquals(3, smartcard.getRemainingRides()); + + list.set(2, hexStringToByteArray("05012160014020014030014040014050012110012170012180000000009000")); + list.set(11, hexStringToByteArray("0501000000216D959E0004F800002A00000000006D959E00001000674E9000")); + list.set(12, hexStringToByteArray("0501000000216D7F0D0004F800002A00000000006D7F0D00007000BEE39000")); + list.set(13, hexStringToByteArray("0501000000216D00050004F800000400000000006CFFF00000400007779000")); + SmartCard s2 = new SmartCard(list); + + assertEquals(2, s2.getRemainingRides()); + + + list.set(2, hexStringToByteArray("05012060014020014030014040014050012110012170012180000000009000")); + list.set(11, hexStringToByteArray("0501000000216D9B2C0004F800002A00000000006D9B2C000060008D809000")); + list.set(12, hexStringToByteArray("0501000000216D959E0004F800002A00000000006D959E00001000674E9000")); + list.set(13, hexStringToByteArray("0501000000216D7F0D0004F800002A00000000006D7F0D00007000BEE39000")); + smartcard = new SmartCard(list); + + assertEquals(1, smartcard.getRemainingRides()); + + + list.set(2, hexStringToByteArray("05012060014020014030014040014050012010012070012180000000009000")); + list.set(11, hexStringToByteArray("0501000000216DA6820004F800002A00000000006DA68200008000F9249000")); + list.set(12, hexStringToByteArray("0501000000216D9B2C0004F800002A00000000006D9B2C000060008D809000")); + list.set(13, hexStringToByteArray("0501000000216D959E0004F800002A00000000006D959E00001000674E9000")); + smartcard = new SmartCard(list); + + assertEquals(0, smartcard.getRemainingRides()); + } } \ No newline at end of file diff --git a/nfc.svg b/nfc.svg index 6956474..43bcca8 100755 --- a/nfc.svg +++ b/nfc.svg @@ -1,60 +1,60 @@ image/svg+xml \ No newline at end of file + style="fill:#515a66;fill-opacity:1" /> \ No newline at end of file