diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..39fb081 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..7ac24c7 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..3963879 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..4b9c4f4 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..e6a5826 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,29 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 26 + defaultConfig { + applicationId "org.dslul.ticketreader" + minSdkVersion 15 + targetSdkVersion 26 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + 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:26.1.0' + implementation 'com.android.support.constraint:constraint-layout:1.0.2' + implementation 'com.android.support:design:26.1.0' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'com.android.support.test:runner:1.0.1' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/src/androidTest/java/org/dslul/ticketreader/ExampleInstrumentedTest.java b/app/src/androidTest/java/org/dslul/ticketreader/ExampleInstrumentedTest.java new file mode 100644 index 0000000..951f201 --- /dev/null +++ b/app/src/androidTest/java/org/dslul/ticketreader/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package org.dslul.ticketreader; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("org.dslul.ticketreader", appContext.getPackageName()); + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..7f0191a --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + \ 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 new file mode 100644 index 0000000..495167c --- /dev/null +++ b/app/src/main/java/org/dslul/ticketreader/MainActivity.java @@ -0,0 +1,213 @@ +package org.dslul.ticketreader; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.util.Log; +import android.view.View; +import android.view.Menu; +import android.view.MenuItem; + +import android.widget.Button; +import android.widget.TextView; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.concurrent.TimeUnit; +import android.content.ClipboardManager; +import android.content.ClipData; +import android.content.Context; + +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; + + + +public class MainActivity extends AppCompatActivity { + + private NfcAdapter mNfcAdapter; + private IntentFilter tech; + private IntentFilter[] intentFiltersArray; + private PendingIntent pendingIntent; + private Intent intent; + private AlertDialog alertDialog; + + private String pages = "ERROR"; + + private TextView dataout; + + private static final int ACTION_NONE = 0; + private static final int ACTION_READ = 1; + private int scanAction; + + // list of NFC technologies detected: + private final String[][] techListsArray = new String[][] { + new String[] { + //MifareUltralight.class.getName(), + NfcA.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); + + mNfcAdapter = NfcAdapter.getDefaultAdapter(this); + if (mNfcAdapter == null) { + // Stop here, we definitely need NFC + Toast.makeText(this, "Questo dispositivo non supporta la tecnologia NFC.", Toast.LENGTH_LONG).show(); + finish(); + return; + } + + if (!mNfcAdapter.isEnabled()) { + Toast.makeText(this, "NFC disabilitato. Attiva l'NFC e torna indietro.", 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); + + scanAction = ACTION_READ; + + dataout = new TextView(this); + } + + @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)) { + + String mTextBufferText = "aa"; + + NfcThread nfcThread = new NfcThread(intent, scanAction, mTextBufferText, mTextBufferHandler, mToastShortHandler, mToastLongHandler, mShowInfoDialogHandler); + nfcThread.start(); + + scanAction = ACTION_READ; + } + } + + private Handler mTextBufferHandler = new Handler() { + public void handleMessage(Message msg) { + pages = (String)msg.obj; + if(pages != "ERROR") { + dataout = (TextView) findViewById(R.id.outdata); + Parser parser = new Parser(pages); + dataout.setText("Data obliterazione: " + parser.getDate() + + System.getProperty("line.separator") + + "Corse residue: " + parser.getRemainingRides()); + } + } + }; + + private Handler mToastShortHandler = new Handler() { + public void handleMessage(Message msg) { + String text = (String)msg.obj; + Toast.makeText(MainActivity.this, text, Toast.LENGTH_SHORT).show(); + } + }; + + private Handler mToastLongHandler = new Handler() { + public void handleMessage(Message msg) { + String text = (String)msg.obj; + Toast.makeText(MainActivity.this, text, Toast.LENGTH_LONG).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) { + alertDialog = showAlertDialog("Semplice applicazione opensource per visualizzare le " + + "corse rimanenti nei biglietti GTT."); + alertDialog.show(); + 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(); + scanAction = ACTION_READ; + } + }; + + alertDialog = new AlertDialog.Builder(this) + .setTitle("Informazioni") + //.setIcon(R.drawable.ic_launcher) + .setMessage(message) + .create(); + + alertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { + + public void onCancel(DialogInterface dialog) { + scanAction = ACTION_READ; + } + }); + + return alertDialog; + } + +} diff --git a/app/src/main/java/org/dslul/ticketreader/NfcThread.java b/app/src/main/java/org/dslul/ticketreader/NfcThread.java new file mode 100644 index 0000000..240e321 --- /dev/null +++ b/app/src/main/java/org/dslul/ticketreader/NfcThread.java @@ -0,0 +1,269 @@ +package org.dslul.ticketreader; + +import java.io.IOException; + +import android.content.Intent; +import android.nfc.NfcAdapter; +import android.nfc.Tag; +import android.nfc.tech.NfcA; +import android.os.Handler; +import android.os.Message; + +//import android.util.Log; + +//code from http://www.emutag.com/soft.php + +public class NfcThread extends Thread { + private static final int ACTION_NONE = 0; + private static final int ACTION_READ = 1; + private static final int ACTION_WRITE = 2; + + private Intent intent; + private int scanAction; + private String mTextBufferText; + private Handler mTextBufferHandler, mToastShortHandler, mToastLongHandler, mShowInfoDialogHandler; + + private byte[] readBuffer = new byte[1024]; // maximum theoretical capacity of MIFARE Ultralight + private byte[] toWriteBuffer = new byte[1024]; + + NfcThread( + Intent intent, + int scanAction, + String mTextBufferText, + Handler mTextBufferHandler, Handler mToastShortHandler, Handler mToastLongHandler, Handler mShowInfoDialogHandler + ) { + this.intent = intent; + this.scanAction = scanAction; + this.mTextBufferText = mTextBufferText; + this.mTextBufferHandler = mTextBufferHandler; + this.mToastShortHandler = mToastShortHandler; + this.mToastLongHandler = mToastLongHandler; + this.mShowInfoDialogHandler = mShowInfoDialogHandler; + } + + public void run() { + final Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + + if (scanAction == ACTION_NONE) { + showToastLong("Please select READ or WRITE before scanning tag"); + return; + } + + final NfcA mfu = NfcA.get(tagFromIntent); + + if (mfu == null) { + showToastLong("Tag does not support ISO 14443-A!"); + return; + } + + byte[] ATQA = mfu.getAtqa(); + + if (mfu.getSak() != 0x00 || ATQA.length != 2 || ATQA[0] != 0x44 || ATQA[1] != 0x00) { + showToastLong("Tag is not MIFARE Ultralight® - compatible!"); + return; + } + + int pagesRead; + + try { + //Log.i("position", "read data"); + + if (scanAction == ACTION_READ) { + mfu.connect(); + pagesRead = rdNumPages(mfu, 0); // 0 for no limit (until error) + mfu.close(); + + String content = ""; + byte[] mfuPage = new byte[4]; + for (int i = 0; i < pagesRead * 4; i += 4) { + System.arraycopy(readBuffer, i, mfuPage, 0, 4); + content = content + ByteArrayToHexString(mfuPage) + System.getProperty("line.separator"); + } + if(pagesRead >= 16) { + showToastShort("Biglietto letto correttamente."); + setTextBuffer(content); + } else { + showToastShort("Lettura fallita, riprovare"); + setTextBuffer("ERROR"); + } + } + } + catch (Exception e) { + showToastLong("Errore di comunicazione. Riprova"); + } + } + + private void setTextBuffer(String text) { + Message msg = new Message(); + msg.obj = text; + mTextBufferHandler.sendMessage(msg); + } + + private void showToastShort(String text) { + Message msg = new Message(); + msg.obj = text; + mToastShortHandler.sendMessage(msg); + } + + private void showToastLong(String text) { + Message msg = new Message(); + msg.obj = text; + mToastLongHandler.sendMessage(msg); + } + + private void showInfoDialog(String text) { + Message msg = new Message(); + msg.obj = text; + mShowInfoDialogHandler.sendMessage(msg); + } + + /* + private int rdAllPages(NfcA mfu) { + int pagesRead = 0; + while (rdPages(mfu, pagesRead) == 0) { + pagesRead += 4; + if (pagesRead == 256) break; + } + return pagesRead; + } + */ + + private int rdNumPages(NfcA mfu, int num) { + int pagesRead = 0; + + while (rdPages(mfu, pagesRead) == 0) { + pagesRead++; + if (pagesRead == num || pagesRead == 256) break; + } + return pagesRead; + } + + /* + private int rdNumPages(NfcA mfu, int num) { + int pagesRead = 0; + + // align number of pages to a multiple of 4 + num += 3; + num >>>= 2; // unsigned shift + num <<= 2; + + while (rdPages(mfu, pagesRead) == 0) { + pagesRead += 4; + if (pagesRead == num || pagesRead == 256) break; + } + return pagesRead; + } + */ + + // first failure (NAK) causes response 0x00 (or possibly other 1-byte values) + // second failure (NAK) causes transceive() to throw IOException + private byte rdPages(NfcA tag, int pageOffset) { + byte[] cmd = {0x30, (byte)pageOffset}; + byte[] response = new byte[16]; + try { response = tag.transceive(cmd); } + catch (IOException e) { return 1; } + if (response.length != 16) return 1; + //System.arraycopy(response, 0, readBuffer, pageOffset * 4, 16); + System.arraycopy(response, 0, readBuffer, pageOffset * 4, 4); + return 0; + } + + // first failure (NAK) causes transceive() to throw IOException + /* + private byte wrPage(NfcA tag, int pageOffset) { + byte[] cmd = {(byte)0xA2, (byte)pageOffset, 0x00, 0x00, 0x00, 0x00}; + System.arraycopy(toWriteBuffer, pageOffset * 4, cmd, 2, 4); + try { + Log.i("TRANS START", Integer.toString(pageOffset)); + tag.transceive(cmd); + Log.i("TRANS END", Integer.toString(pageOffset)); + } + catch (IOException e) { return 1; } + return 0; + } + */ + + private byte wrPage(NfcA mfu, int pageOffset) { + byte[] cmd = {(byte)0xA2, (byte)pageOffset, 0x00, 0x00, 0x00, 0x00}; + System.arraycopy(toWriteBuffer, pageOffset * 4, cmd, 2, 4); + //byte[] data = {0x00, 0x00, 0x00, 0x00}; + //System.arraycopy(toWriteBuffer, pageOffset * 4, data, 0, 4); + //byte errors = 0; + + //final MifareUltralight mfu = MifareUltralight.get(tag); + + try { + //mfu.connect(); + //Log.i("TRANS START", Integer.toString(pageOffset)); + //mfu.writePage(pageOffset, data); + mfu.transceive(cmd); + //Log.i("TRANS END", Integer.toString(pageOffset)); + //mfu.close(); + } + catch (final IOException e) { return 1; } + /* + finally { + try { mfu.close(); } + catch (final Exception e) {} + } + */ + + return 0; + } + + // first failure (NAK) causes transceive() to throw IOException + private byte wrPageCompat(NfcA tag, int pageOffset) { + byte[] cmd1 = {(byte)0xA0, (byte)pageOffset}; + byte[] cmd2 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + System.arraycopy(toWriteBuffer, pageOffset * 4, cmd2, 0, 4); + try { + tag.transceive(cmd1); + tag.transceive(cmd2); + } + catch (IOException e) { return 1; } + return 0; + } + + private String ByteArrayToHexString(byte[] inarray) { + int i, j, in; + String [] hex = {"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"}; + String out= ""; + for(j = 0 ; j < inarray.length ; ++j) { + in = (int) inarray[j] & 0xff; + i = (in >> 4) & 0x0f; + out += hex[i]; + i = in & 0x0f; + out += hex[i]; + } + return out; + } + + private int HexStringToByteArray(String instring, byte[] outarray, int outoffset) { + int errors = 0; + byte[] nibbles = new byte[2]; + for (int i = 0; i < instring.length(); i += 2) { + nibbles[0] = (byte)instring.charAt(i+0); + nibbles[1] = (byte)instring.charAt(i+1); + if (notHex(nibbles[0])) errors = 1; + if (notHex(nibbles[1])) errors = 1; + outarray[outoffset] = (byte)((hex2bin(nibbles[0]) << 4) | hex2bin(nibbles[1])); + outoffset++; + } + return errors; + } + + private boolean notHex(byte inchar) { + if (inchar >= '0' && inchar <= '9') return false; + if (inchar >= 'a' && inchar <= 'f') return false; + if (inchar >= 'A' && inchar <= 'F') return false; + return true; + } + + private byte hex2bin(byte inchar) { + if (inchar > 'Z') inchar -= ' '; + if (inchar > '9') inchar -= 7; + inchar &= 0x0f; + return inchar; + } +} + diff --git a/app/src/main/java/org/dslul/ticketreader/Parser.java b/app/src/main/java/org/dslul/ticketreader/Parser.java new file mode 100644 index 0000000..97e3d22 --- /dev/null +++ b/app/src/main/java/org/dslul/ticketreader/Parser.java @@ -0,0 +1,79 @@ +package org.dslul.ticketreader; + +import android.util.Log; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.concurrent.TimeUnit; + + +public class Parser { + + private String pages; + private String date; + + public Parser(String data) { + //if(data == null) + + this.pages = data; + this.date = this.pages.substring(90, 96); + } + + public String getDate() { + String startingDate = "05/01/01 00:00:00"; + SimpleDateFormat format = new SimpleDateFormat("yy/MM/dd HH:mm:ss"); + Date date = null; + try { + date = format.parse(startingDate); + } catch (ParseException e) { + e.printStackTrace(); + } + return DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT) + .format(addMinutesToDate(Long.parseLong(this.date, 16), date)); + } + + + //TODO: corse in metropolitana (forse bit più significativo pag. 3) + public int getRemainingRides() { + int tickettype = (int)getBytesFromPage(5, 0, 1); + int tickets; + if(tickettype == 3) { //extraurbano + tickets = (int) (~getBytesFromPage(3, 0, 4)); + } else { + tickets = (int)(~getBytesFromPage(3, 2, 2)) + & 0xFFFF; + } + return Integer.bitCount(tickets); + } + + + + private long getBytesFromPage(int page, int offset, int bytesnum) { + return Long.parseLong( + pages.substring(9 * page + offset * 2, 9 * page + offset * 2 + bytesnum * 2), 16); + } + + + + private static Date addMinutesToDate(long minutes, Date beforeTime){ + final long ONE_MINUTE_IN_MILLIS = 60000; + + long curTimeInMs = beforeTime.getTime(); + Date afterAddingMins = new Date(curTimeInMs + (minutes * ONE_MINUTE_IN_MILLIS)); + return afterAddingMins; + } + + + private static byte[] hexStringToByteArray(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i+1), 16)); + } + return data; +} + +} diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..c7bd21d --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..d5fccc5 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..d512d40 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml new file mode 100644 index 0000000..15e0c26 --- /dev/null +++ b/app/src/main/res/layout/content_main.xml @@ -0,0 +1,33 @@ + + + + + + + + diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml new file mode 100644 index 0000000..5885961 --- /dev/null +++ b/app/src/main/res/menu/menu_main.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..5d1d91d Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..a828c1e Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..ba08dcb Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..baa3201 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..0896c5f Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..fbf04c7 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..499676e Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..b9369c5 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..dd0a681 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..419ce9c Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..3ab3e9c --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #3F51B5 + #303F9F + #FF4081 + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..59a0b0c --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,3 @@ + + 16dp + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..1dd513b --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + GTT Ticket Reader + Info + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..b908517 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,20 @@ + + + + + + + +