diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index cf0869b..ba473a4 100755 Binary files a/.idea/caches/build_file_checksums.ser and b/.idea/caches/build_file_checksums.ser differ diff --git a/app/build.gradle b/app/build.gradle index 7936434..9604cf9 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,34 +1,34 @@ apply plugin: 'com.android.application' android { compileSdkVersion 27 defaultConfig { applicationId "org.dslul.ticketreader" minSdkVersion 15 targetSdkVersion 27 - versionCode 14 - versionName "2.0" + versionCode 16 + versionName "2.0beta3" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:27.1.1' implementation 'com.android.support:customtabs:27.1.1' - implementation 'com.android.support.constraint:constraint-layout:1.1.2' + implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'com.android.support:design:27.1.1' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' implementation 'com.google.android.gms:play-services-ads:15.0.1' implementation 'com.android.support:cardview-v7:27.1.1' implementation 'com.yarolegovich:lovely-dialog:1.1.0' } diff --git a/app/release/output.json b/app/release/output.json index e305b20..920a5a5 100755 --- a/app/release/output.json +++ b/app/release/output.json @@ -1 +1 @@ -[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":13,"versionName":"1.9.2","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] \ No newline at end of file +[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":16,"versionName":"2.0beta3","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e5c0b6e..825b3f0 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,33 +1,35 @@ + \ 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 34b9695..8f3df93 100755 --- a/app/src/main/java/org/dslul/ticketreader/MainActivity.java +++ b/app/src/main/java/org/dslul/ticketreader/MainActivity.java @@ -1,394 +1,404 @@ 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()); + new LovelyInfoDialog(this) + .setTopColorRes(R.color.darkBlueGrey) + .setIcon(R.drawable.ic_info_outline_white_36dp) + //This will add Don't show again checkbox to the dialog. You can pass any ID as argument + .setNotShowAgainOptionEnabled(0) + .setNotShowAgainOptionChecked(false) + .setTitle("BETA") + .setMessage("Questa versione รจ una beta e potrebbe restituire risultati sbagliati. Segnalare per favore ogni incongruenza (insieme ad una copia del contenuto della carta) all'indirizzo email specificato nelle informazioni. Grazie.") + .show(); + } @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); - if(smartcard.isSubscription()) { + if(smartcard.hasSubscriptions() && !smartcard.hasTickets()) { dataLabel.setText(R.string.expire_date); tipologia.setText(smartcard.getSubscriptionName()); - dataObliterazione.setText(smartcard.getDate()); + dataObliterazione.setText(smartcard.getExpireDate()); - if(smartcard.isExpired()) { + if(smartcard.isSubscriptionExpired()) { 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(), + } else if(smartcard.hasTickets()){ + createTicketInterface(smartcard.getTicketName(),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()); + ex.printStackTrace(); } } }; 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 dc6e0b9..c02e6b0 100644 --- a/app/src/main/java/org/dslul/ticketreader/SmartCard.java +++ b/app/src/main/java/org/dslul/ticketreader/SmartCard.java @@ -1,322 +1,334 @@ package org.dslul.ticketreader; +import android.util.Log; + 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"); }}; 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 List tickets = new ArrayList<>(); + private List subscriptions = new ArrayList<>(); + private Contract subscription; - private String subscriptionName; private int ridesLeft = 0; private long remainingMins; 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; + subscriptions.add(contract); } if(contract.isTicket()) { - //ridesLeft += 1; - if(isExpired(latestContract.getEndDate())) - isSubscription = false; - //TODO: count tickets in daily 7 carnets + tickets.add(contract); } + } - //isSubscription = contract.isSubscription(); + } - contracts.add(contract); + //get a valid subscription, if there's any + Date latestExpireDate = GttDate.getGttEpoch(); + for (Contract sub : subscriptions) { + if (latestExpireDate.before(sub.getEndDate())) { + latestExpireDate = sub.getEndDate(); + subscription = sub; } } + //actual tickets count ridesLeft = countTickets(efContractList, efEventLogs1, efEventLogs2, efEventLogs3); - //get last validation time long mins = getBytesFromPage(efEventLogs1, 20, 3); + if(mins == 0) + mins = getBytesFromPage(efEventLogs2, 20, 3); + if(mins == 0) + mins = getBytesFromPage(efEventLogs3, 20, 3); validationDate = GttDate.addMinutesToDate(mins, GttDate.getGttEpoch()); Calendar c = Calendar.getInstance(); long diff = (c.getTime().getTime() - validationDate.getTime()) / 60000; 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 pos = abs(contractsList[i+1]) >> 4; + //real pos is given by the position in contractslist + int pos = i/3 + 1; + //check if it's a subscription + int sub = abs(contractsList[i]&0xf0) >> 4; + if(pos != 0 && pos <= 8 && (contractsList[i]&0x0f) != 0 && sub != 0xA) { 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) + if(pos != lognum1 && pos != lognum2 && pos != lognum3) count += 1; } } return count; } - public String getName() { - return type + " - " + latestContract.getTypeName(); + public String getTicketName() { + if(hasTickets()) + return type + " - " + tickets.get(0).getTypeName(); + else + return "Invalid"; } public String getSubscriptionName() { - return type + " - " + subscriptionName; + if(hasSubscriptions()) + return type + " - " + subscription.getTypeName(); + else + return "Invalid"; + } + + public boolean hasTickets() { + return ridesLeft != 0; } - public String getDate() { + public boolean hasSubscriptions() { + return subscriptions.size() != 0; + } + + public String getExpireDate() { return DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT) - .format(latestContract.getEndDate()); + .format(subscription.getEndDate()); } public String getValidationDate() { return DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT) .format(validationDate); } - public boolean isExpired() { + public boolean isSubscriptionExpired() { Calendar c = Calendar.getInstance(); - return c.getTime().after(latestContract.getEndDate()); + return c.getTime().after(subscription.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/test/java/org/dslul/ticketreader/ChipOnPaperUnitTest.java b/app/src/test/java/org/dslul/ticketreader/ChipOnPaperUnitTest.java index ae9d4cd..57d8ada 100755 --- a/app/src/test/java/org/dslul/ticketreader/ChipOnPaperUnitTest.java +++ b/app/src/test/java/org/dslul/ticketreader/ChipOnPaperUnitTest.java @@ -1,108 +1,136 @@ 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(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")); + 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()); + + //1 used ticket + list.set(2, hexStringToByteArray("05012110000000000000000000000000000000000000000000000000009000")); + list.set(11, hexStringToByteArray("0501000000216C9EF30004F800007400000000006C9EB100001000F66B9000")); + list.set(12, hexStringToByteArray("0501000000216C9EC80004F80003E700000000006C9EB100001000E9E29000")); + list.set(13, hexStringToByteArray("0501000000216C9EB10004F800007400000000006C9EB10000100098509000")); + smartcard = new SmartCard(list); + + assertEquals(0, smartcard.getRemainingRides()); + + + //1 used ticket + list.set(2, hexStringToByteArray("05012110000000000000000000000000000000000000000000000000009000")); + list.set(11, hexStringToByteArray("0501000000216C9EF30004F800007400000000006C9EB100001000F66B9000")); + list.set(12, hexStringToByteArray("00000000000000000000000000000000000000000000000000000000009000")); + list.set(13, hexStringToByteArray("00000000000000000000000000000000000000000000000000000000009000")); smartcard = new SmartCard(list); assertEquals(0, smartcard.getRemainingRides()); + + + //expired subscriptions only + list.set(2, hexStringToByteArray("0501A10001000001A100000000000000000000000000000000000000009000")); + list.set(11, hexStringToByteArray("0501030258216C41130004F800007400000000006C355D0000300017D89000")); + list.set(12, hexStringToByteArray("0501030258216C3F8A0004F800000F00000000006C355D000030002EE19000")); + list.set(13, hexStringToByteArray("0501030258216C356A0004F800000A00000000006C355D000030007F249000")); + smartcard = new SmartCard(list); + + assertEquals(false, smartcard.hasTickets()); } } \ No newline at end of file