diff --git a/.gitignore b/.gitignore
index 603b140..1249651 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,14 +1,5 @@
*.iml
-.gradle
-/local.properties
-/.idea/caches
-/.idea/libraries
-/.idea/modules.xml
-/.idea/workspace.xml
-/.idea/navEditor.xml
-/.idea/assetWizardSettings.xml
-.DS_Store
-/build
-/captures
-.externalNativeBuild
-.cxx
+.gradle/
+.idea/
+build/
+local.properties
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
deleted file mode 100644
index 88ea3aa..0000000
--- a/.idea/codeStyles/Project.xml
+++ /dev/null
@@ -1,122 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- xmlns:android
-
- ^$
-
-
-
-
-
-
-
-
- xmlns:.*
-
- ^$
-
-
- BY_NAME
-
-
-
-
-
-
- .*:id
-
- http://schemas.android.com/apk/res/android
-
-
-
-
-
-
-
-
- .*:name
-
- http://schemas.android.com/apk/res/android
-
-
-
-
-
-
-
-
- name
-
- ^$
-
-
-
-
-
-
-
-
- style
-
- ^$
-
-
-
-
-
-
-
-
- .*
-
- ^$
-
-
- BY_NAME
-
-
-
-
-
-
- .*
-
- http://schemas.android.com/apk/res/android
-
-
- ANDROID_ATTRIBUTE_ORDER
-
-
-
-
-
-
- .*
-
- .*
-
-
- BY_NAME
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
deleted file mode 100644
index 79ee123..0000000
--- a/.idea/codeStyles/codeStyleConfig.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
deleted file mode 100644
index 3e3960b..0000000
--- a/.idea/gradle.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index 4adab6b..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
-<<<<<<< HEAD
-
-
-
-
- 1.8
-
-
-
-
-
-
-
-=======
->>>>>>> 6164b7ab9283971583ecc0a415a805140fc8b571
-
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
deleted file mode 100644
index 7f68460..0000000
--- a/.idea/runConfigurations.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 94a25f7..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..5456a21
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,55 @@
+# General
+language: android
+jdk: oraclejdk8
+dist: xenial
+os: linux
+
+# Environment variables
+env:
+ global:
+ - ANDROID_API_LEVEL=29
+ - ANDROID_BUILD_TOOLS_VERSION=29.0.3
+
+# Android
+android:
+ components:
+ - tools
+ - platform-tools
+ - build-tools-$ANDROID_BUILD_TOOLS_VERSION
+ - android-$ANDROID_API_LEVEL
+ - extra-google-google_play_services
+ - extra-google-m2repository
+ - extra-android-m2repository
+ - addon-google_apis-google-$ANDROID_API_LEVEL
+ licenses:
+ - 'android-sdk-preview-license-.+'
+ - 'android-sdk-license-.+'
+ - 'google-gdk-license-.+'
+
+# Caching
+before_cache:
+ - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
+ - rm -fr $HOME/.gradle/caches/*/plugin-resolution/
+cache:
+ directories:
+ - $HOME/.gradle/caches/
+ - $HOME/.gradle/wrapper/
+ - $HOME/.android/build-cache
+
+# Make gradle executable
+before_script:
+ - chmod +x gradlew
+
+# Stages
+stages:
+ - compile
+ - test
+
+# Jobs
+jobs:
+ include:
+ - stage: compile
+ script: ./gradlew assembleDebug
+ - stage: test
+ script: ./gradlew test
+ - script: ./gradlew ktlintReleaseFormat
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 4b7fc37..c5d93ef 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,14 +1,17 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-kapt'
+apply plugin: 'org.jlleitschuh.gradle.ktlint'
+apply plugin: 'androidx.navigation.safeargs'
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
- applicationId "com.example.easybill"
- minSdkVersion 28
+ applicationId "com.easybill"
+ minSdkVersion 29
targetSdkVersion 29
versionCode 1
versionName "1.0"
@@ -23,15 +26,51 @@ android {
}
}
+ dataBinding {
+ enabled = true
+ }
+
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
- implementation 'androidx.core:core-ktx:1.2.0'
+ implementation 'androidx.core:core-ktx:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
- testImplementation 'junit:junit:4.12'
+ testImplementation 'junit:junit:4.13'
+ implementation 'androidx.legacy:legacy-support-v4:1.0.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+ implementation 'com.google.android.material:material:1.1.0'
+
+ // Room
+ def room_version = "2.2.5"
+ implementation "androidx.room:room-runtime:$room_version"
+ implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+ kapt "androidx.room:room-compiler:$room_version"
+
+ // ViewModel
+ implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
+
+ // Coroutines
+ def coroutine_version = '1.1.0'
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
+
+ // Navigation
+ implementation "android.arch.navigation:navigation-fragment-ktx:$navigationVersion"
+ implementation "android.arch.navigation:navigation-ui-ktx:$navigationVersion"
+ implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
+
+ // Timber
+ implementation 'com.jakewharton.timber:timber:4.7.1'
+
+ // Recyclerview
+ implementation "androidx.recyclerview:recyclerview:1.1.0"
}
+
+ktlint {
+ android.set(true)
+ outputColorName.set("RED")
+}
\ No newline at end of file
diff --git a/app/src/androidTest/java/com/example/easybill/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/easybill/ExampleInstrumentedTest.kt
similarity index 95%
rename from app/src/androidTest/java/com/example/easybill/ExampleInstrumentedTest.kt
rename to app/src/androidTest/java/com/easybill/ExampleInstrumentedTest.kt
index 84505f5..feeeec3 100644
--- a/app/src/androidTest/java/com/example/easybill/ExampleInstrumentedTest.kt
+++ b/app/src/androidTest/java/com/easybill/ExampleInstrumentedTest.kt
@@ -1,4 +1,4 @@
-package com.example.easybill
+package com.easybill
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
diff --git a/app/src/androidTest/java/com/easybill/database/BillTest.kt b/app/src/androidTest/java/com/easybill/database/BillTest.kt
new file mode 100644
index 0000000..44805c4
--- /dev/null
+++ b/app/src/androidTest/java/com/easybill/database/BillTest.kt
@@ -0,0 +1,107 @@
+package com.easybill.database
+
+import android.content.Context
+import androidx.room.Room
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.easybill.database.dao.HeadDao
+import com.easybill.database.model.Head
+import org.hamcrest.CoreMatchers.*
+import org.junit.After
+import org.junit.Assert.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.io.IOException
+
+@RunWith(AndroidJUnit4::class)
+class BillTest {
+ private lateinit var headDao: HeadDao
+ private lateinit var db: EasyBillDatabase
+
+ @Before
+ fun createDb() {
+ val context = ApplicationProvider.getApplicationContext()
+ db = Room.inMemoryDatabaseBuilder(
+ context, EasyBillDatabase::class.java).build()
+ headDao = db.getHeadDao()
+ }
+
+ @After
+ @Throws(IOException::class)
+ fun closeDb() {
+ db.close()
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun insertAndGetTenBills() {
+ val numBills = 10
+
+ for (i in 1..numBills) {
+ // create a new head with test-data
+ val bill = Head()
+ bill.address = "TestAddress#$i"
+ bill.storeName = "StoreName#$i"
+ bill.salesTax = i.toDouble()
+
+ // insert the bill and get it
+ bill.id = headDao.insert(bill)
+ val actual = headDao.getById(bill.id)
+
+ // verify results
+ assertThat(actual.address, equalTo(bill.address))
+ assertThat(actual.storeName, equalTo(bill.storeName))
+ assertThat(actual.salesTax, equalTo(bill.salesTax))
+ }
+
+ // get all bills
+ val bs = headDao.getAll()
+ assertThat(bs.size, equalTo(numBills))
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun insertAndUpdateBill() {
+ // create a new bill with test-data
+ val bill = Head()
+ bill.address = "TestAddress"
+ bill.storeName = "StoreName"
+ bill.salesTax = 19.0
+
+ // insert the bill
+ bill.id = headDao.insert(bill)
+
+ // now change the bill and update it
+ bill.address = "NewTestAddress"
+ bill.storeName = "NewStoreName"
+ bill.salesTax = 20.0
+ headDao.update(bill)
+
+ // get the updated bill and verify
+ val actual = headDao.getById(bill.id)
+ assertThat(actual.address, equalTo(bill.address))
+ assertThat(actual.storeName, equalTo(bill.storeName))
+ assertThat(actual.salesTax, equalTo(bill.salesTax))
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun insertAndDeleteBill() {
+ // create a new bill with test-data
+ val bill = Head()
+ bill.address = "TestAddress"
+ bill.storeName = "StoreName"
+ bill.salesTax = 19.0
+
+ // insert the bill
+ bill.id = headDao.insert(bill)
+
+ // delete the bill
+ headDao.delete(bill)
+
+ // the bill should now be deleted and null must be returned
+ val actual = headDao.getById(bill.id)
+ assertThat(actual, `is`(nullValue()))
+ }
+}
diff --git a/app/src/androidTest/java/com/easybill/database/BillWithItemsTest.kt b/app/src/androidTest/java/com/easybill/database/BillWithItemsTest.kt
new file mode 100644
index 0000000..a9f621e
--- /dev/null
+++ b/app/src/androidTest/java/com/easybill/database/BillWithItemsTest.kt
@@ -0,0 +1,120 @@
+package com.easybill.database
+
+import android.content.Context
+import androidx.room.Room
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.easybill.database.dao.HeadDao
+import com.easybill.database.dao.ItemDao
+import com.easybill.database.model.Item
+import com.easybill.database.model.Head
+import org.hamcrest.CoreMatchers.equalTo
+import org.junit.After
+import org.junit.Assert.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.io.IOException
+
+@RunWith(AndroidJUnit4::class)
+class BillWithItemsTest {
+ private lateinit var itemDao: ItemDao
+ private lateinit var headDao: HeadDao
+ private lateinit var db: EasyBillDatabase
+
+ @Before
+ fun createDb() {
+ val context = ApplicationProvider.getApplicationContext()
+ db = Room.inMemoryDatabaseBuilder(
+ context, EasyBillDatabase::class.java).build()
+ headDao = db.getHeadDao()
+ itemDao = db.getItemDao()
+ }
+
+ @After
+ @Throws(IOException::class)
+ fun closeDb() {
+ db.close()
+ }
+
+
+ @Test
+ @Throws(Exception::class)
+ fun insertBillWithItemsGetBillWithItems() {
+ val numItems = 10
+
+ // create a new bill with test-data
+ val bill = Head()
+ bill.address = "TestAddress"
+ bill.storeName = "StoreName"
+ bill.salesTax = 19.0
+
+ // insert the bill
+ bill.id = headDao.insert(bill)
+
+ // insert numItems items
+ for (i in 1..numItems) {
+
+ // new item with bill id of previously inserted bill
+ val item = Item()
+ item.billId = bill.id
+ item.amount = i.toDouble()
+ item.name = "TestItem#$i"
+ item.nettoPrice = i.toDouble()
+
+ // insert item
+ item.id = itemDao.insert(item)
+ }
+
+ // get bill with items
+ val actual = headDao.getBillById(bill.id)
+
+ // bill should have numItems items
+ assertThat(actual.items.size, equalTo(numItems))
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun insertBillWithItemsDeleteItemsAndGetBillWithItems() {
+ val numItems = 10
+
+ // create a new bill with test-data
+ val bill = Head()
+ bill.address = "TestAddress"
+ bill.storeName = "StoreName"
+ bill.salesTax = 19.0
+
+ // insert the bill
+ bill.id = headDao.insert(bill)
+
+ // insert numItems items
+ var lastInsertedItemId = 0L
+ for (i in 1..numItems) {
+
+ // new item with bill id of previously inserted bill
+ val item = Item()
+ item.billId = bill.id
+ item.amount = i.toDouble()
+ item.name = "TestItem#$i"
+ item.nettoPrice = i.toDouble()
+
+ // insert item
+ item.id = itemDao.insert(item)
+ lastInsertedItemId = item.id
+ }
+
+ // get bill with items
+ var actual = headDao.getBillById(bill.id)
+
+ // bill should have numItems items
+ assertThat(actual.items.size, equalTo(numItems))
+
+ // delete last inserted item
+ val item = itemDao.getById(lastInsertedItemId)
+ itemDao.delete(item)
+
+ // get bill with items, should now have numItems-1 items
+ actual = headDao.getBillById(bill.id)
+ assertThat(actual.items.size, equalTo(numItems-1))
+ }
+}
\ No newline at end of file
diff --git a/app/src/androidTest/java/com/easybill/database/ItemTest.kt b/app/src/androidTest/java/com/easybill/database/ItemTest.kt
new file mode 100644
index 0000000..1a587bb
--- /dev/null
+++ b/app/src/androidTest/java/com/easybill/database/ItemTest.kt
@@ -0,0 +1,107 @@
+package com.easybill.database
+
+import android.content.Context
+import androidx.room.Room
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.easybill.database.dao.ItemDao
+import com.easybill.database.model.Item
+import org.hamcrest.CoreMatchers.*
+import org.junit.After
+import org.junit.Assert.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.io.IOException
+
+@RunWith(AndroidJUnit4::class)
+class ItemTest {
+ private lateinit var itemDao: ItemDao
+ private lateinit var db: EasyBillDatabase
+
+ @Before
+ fun createDb() {
+ val context = ApplicationProvider.getApplicationContext()
+ db = Room.inMemoryDatabaseBuilder(
+ context, EasyBillDatabase::class.java).build()
+ itemDao = db.getItemDao()
+ }
+
+ @After
+ @Throws(IOException::class)
+ fun closeDb() {
+ db.close()
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun insertAndGetTenItems() {
+ val numItems = 10
+
+ for (i in 1..numItems) {
+ // create a new item with test-data
+ val item = Item()
+ item.name = "ItemName#$i"
+ item.amount = i.toDouble()
+ item.nettoPrice = i.toDouble()
+
+ // insert the item and get it
+ item.id = itemDao.insert(item)
+ val actual = itemDao.getById(item.id)
+
+ // verify results
+ assertThat(actual.name, equalTo(item.name))
+ assertThat(actual.amount, equalTo(item.amount))
+ assertThat(actual.nettoPrice, equalTo(item.nettoPrice))
+ }
+
+ // get all items
+ val items = itemDao.getAll()
+ assertThat(items.size, equalTo(numItems))
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun insertAndUpdateItem() {
+ // create a new item with test-data
+ val item = Item()
+ item.name = "TestName"
+ item.amount = 1.0
+ item.nettoPrice = 1.0
+
+ // insert the item
+ item.id = itemDao.insert(item)
+
+ // now change the item and update it
+ item.name = "NewTestName"
+ item.amount = 2.0
+ item.nettoPrice = 10.0
+ itemDao.update(item)
+
+ // get the updated item and verify
+ val actual = itemDao.getById(item.id)
+ assertThat(actual.name, equalTo(item.name))
+ assertThat(actual.amount, equalTo(item.amount))
+ assertThat(actual.nettoPrice, equalTo(item.nettoPrice))
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun insertAndDeleteItem() {
+ // create a new item with test-data
+ val item = Item()
+ item.name = "TestName"
+ item.amount = 1.0
+ item.nettoPrice = 1.0
+
+ // insert the item
+ item.id = itemDao.insert(item)
+
+ // delete the item
+ itemDao.delete(item)
+
+ // the item should now be deleted and null must be returned
+ val actual = itemDao.getById(item.id)
+ assertThat(actual, `is`(nullValue()))
+ }
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 66c8fd3..249fd8f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,8 +1,9 @@
+ package="com.easybill">
-
diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png
new file mode 100644
index 0000000..07431c7
Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ
diff --git a/app/src/main/java/com/easybill/EasyBillApplication.kt b/app/src/main/java/com/easybill/EasyBillApplication.kt
new file mode 100644
index 0000000..8ffc207
--- /dev/null
+++ b/app/src/main/java/com/easybill/EasyBillApplication.kt
@@ -0,0 +1,16 @@
+package com.easybill
+
+import android.app.Application
+import timber.log.Timber
+
+/**
+ * Entry-point of the application.
+ */
+class EasyBillApplication : Application() {
+ override fun onCreate() {
+ super.onCreate()
+
+ // setup timber for logging
+ Timber.plant(Timber.DebugTree())
+ }
+}
diff --git a/app/src/main/java/com/easybill/MainActivity.kt b/app/src/main/java/com/easybill/MainActivity.kt
new file mode 100644
index 0000000..3eca750
--- /dev/null
+++ b/app/src/main/java/com/easybill/MainActivity.kt
@@ -0,0 +1,156 @@
+package com.easybill
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.lifecycle.ViewModelProvider
+import androidx.navigation.findNavController
+import androidx.navigation.ui.NavigationUI
+import com.easybill.database.Converters
+import com.easybill.database.EasyBillDatabase
+import com.easybill.database.model.Bill
+import com.easybill.database.model.Head
+import com.easybill.database.model.Item
+import com.easybill.viewmodel.EasyBillViewModel
+import com.easybill.viewmodel.EasyBillViewModelFactory
+import java.util.Objects.requireNonNull
+
+/**
+ * Main-Activity of this application.
+ */
+class MainActivity : AppCompatActivity() {
+
+ // keeps state when the activity gets re-loaded on device configuration change
+ private lateinit var viewModel: EasyBillViewModel
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+
+ // setup navigation
+ val navController = this.findNavController(R.id.myNavHostFragment)
+ NavigationUI.setupActionBarWithNavController(this, navController)
+
+ // get bill-dao and create view-model
+ val headDao = EasyBillDatabase.getInstance(application).getHeadDao()
+ val itemDao = EasyBillDatabase.getInstance(application).getItemDao()
+ val billDao = EasyBillDatabase.getInstance(application).getBillDao()
+
+ // create view-model
+ val viewModelFactory = EasyBillViewModelFactory(headDao, itemDao, billDao, application)
+ viewModel = ViewModelProvider(this, viewModelFactory).get(EasyBillViewModel::class.java)
+
+ // fill database with mock-data
+ // fillDatabase()
+
+ // clear database-tables
+ // clearDatabase()
+ }
+
+ override fun onSupportNavigateUp(): Boolean {
+ val navController = this.findNavController(R.id.myNavHostFragment)
+ return navController.navigateUp()
+ }
+
+ private fun fillDatabase() {
+ val millisPerDay = 24 * 60 * 60 * 1000
+ val converter = Converters()
+ // val application = requireNonNull(this).application
+ // val headDao = EasyBillDatabase.getInstance(application).getHeadDao()
+ // val itemDao = EasyBillDatabase.getInstance(application).getItemDao()
+ // val billDao = EasyBillDatabase.getInstance(application).getBillDao()
+ // val viewModel = EasyBillViewModel(headDao, itemDao, billDao, application)
+
+ /*
+ * Bill#1
+ */
+ val headOne = Head()
+ headOne.storeName = "Media Markt"
+ headOne.address = "Fakestreet 1234, 80801 München"
+ headOne.time =
+ converter.localDateTimeFromTimestamp(System.currentTimeMillis() - millisPerDay)!!
+ // items #1.1
+ val itemOneOne = Item()
+ itemOneOne.amount = 1.0
+ itemOneOne.tax = 0.19
+ itemOneOne.name = "USB-Stick erster Güte"
+ itemOneOne.nettoPrice = 13.37
+ // items #1.2
+ val itemOneTwo = Item()
+ itemOneTwo.amount = 3.0
+ itemOneTwo.tax = 0.19
+ itemOneTwo.name = "Wirklich schnelle SSD"
+ itemOneTwo.nettoPrice = 199.00
+ val billOne = Bill(
+ headOne,
+ listOf(itemOneOne, itemOneTwo)
+ )
+ viewModel.addBill(billOne)
+
+ /*
+ * Bill#2
+ */
+ val headTwo = Head()
+ headTwo.storeName = "Edeka"
+ headTwo.address = "Irgendwostr. 13, 13370 Frankfurt"
+ headTwo.time = converter
+ .localDateTimeFromTimestamp(System.currentTimeMillis() - 2 * millisPerDay)!!
+ // items #2.1
+ val itemTwoOne = Item()
+ itemTwoOne.amount = 1.0
+ itemTwoOne.tax = 0.07
+ itemTwoOne.name = "Käsebrot"
+ itemTwoOne.nettoPrice = 7.99
+ // items #2.2
+ val itemTwoTwo = Item()
+ itemTwoTwo.amount = 3.0
+ itemTwoTwo.tax = 0.07
+ itemTwoTwo.name = "Milch von der Kuh"
+ itemTwoTwo.nettoPrice = 1.39
+ // items #2.3
+ val itemTwoThree = Item()
+ itemTwoThree.amount = 3.0
+ itemTwoThree.tax = 0.07
+ itemTwoThree.name = "Magerquark"
+ itemTwoThree.nettoPrice = 0.89
+ val billTwo = Bill(
+ headTwo,
+ listOf(itemTwoOne, itemTwoTwo, itemTwoThree)
+ )
+ viewModel.addBill(billTwo)
+
+ /*
+ * Bill#3
+ */
+ val headThree = Head()
+ headThree.storeName = "McDonalds"
+ headThree.address = "Hipstersquare 13, 08574 Bonn"
+ headThree.time = converter
+ .localDateTimeFromTimestamp(System.currentTimeMillis() - 3 * millisPerDay)!!
+ // items #3.1
+ val itemThreeOne = Item()
+ itemThreeOne.amount = 199.0
+ itemThreeOne.tax = 0.07
+ itemThreeOne.name = "Cheeseburger"
+ itemThreeOne.nettoPrice = 1.0
+ // items #3.2
+ val itemThreeTwo = Item()
+ itemThreeTwo.amount = 1.0
+ itemThreeTwo.tax = 0.07
+ itemThreeTwo.name = "Diät-Cola"
+ itemThreeTwo.nettoPrice = 1.0
+ val billThree = Bill(
+ headThree,
+ listOf(itemThreeOne, itemThreeTwo)
+ )
+ viewModel.addBill(billThree)
+ }
+
+ private fun clearDatabase() {
+ val application = requireNonNull(this).application
+ val headDao = EasyBillDatabase.getInstance(application).getHeadDao()
+ val itemDao = EasyBillDatabase.getInstance(application).getItemDao()
+ val billDao = EasyBillDatabase.getInstance(application).getBillDao()
+ val viewModel = EasyBillViewModel(headDao, itemDao, billDao, application)
+ viewModel.deleteAllBills()
+ }
+}
diff --git a/app/src/main/java/com/easybill/database/Converters.kt b/app/src/main/java/com/easybill/database/Converters.kt
new file mode 100644
index 0000000..70cdc6a
--- /dev/null
+++ b/app/src/main/java/com/easybill/database/Converters.kt
@@ -0,0 +1,37 @@
+package com.easybill.database
+
+import androidx.room.TypeConverter
+import java.time.Instant
+import java.time.LocalDateTime
+import java.time.ZoneId
+import java.util.TimeZone
+
+/**
+ * Various converters to translate non-primitive types to a fitting
+ * database-representation.
+ */
+class Converters {
+
+ /**
+ * Converts a timestamp (milliseconds) to a LocalDateTime of the default time-zone.
+ */
+ @TypeConverter
+ fun localDateTimeFromTimestamp(value: Long?): LocalDateTime? {
+ return value?.let {
+ LocalDateTime.ofInstant(
+ Instant.ofEpochMilli(value),
+ TimeZone.getDefault().toZoneId()
+ )
+ }
+ }
+
+ /**
+ * Converts a LocalDateTime to a timestamp (milliseconds) of the default time-zone.
+ */
+ @TypeConverter
+ fun localDateTimeToTimestamp(value: LocalDateTime?): Long? {
+ return value?.let {
+ value.atZone(ZoneId.systemDefault()).toEpochSecond()
+ }
+ }
+}
diff --git a/app/src/main/java/com/easybill/database/EasyBillDatabase.kt b/app/src/main/java/com/easybill/database/EasyBillDatabase.kt
new file mode 100644
index 0000000..a4948a3
--- /dev/null
+++ b/app/src/main/java/com/easybill/database/EasyBillDatabase.kt
@@ -0,0 +1,70 @@
+package com.easybill.database
+
+import android.content.Context
+import androidx.room.Database
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import androidx.room.TypeConverters
+import com.easybill.database.dao.BillDao
+import com.easybill.database.dao.HeadDao
+import com.easybill.database.dao.ItemDao
+import com.easybill.database.model.Head
+import com.easybill.database.model.Item
+
+/**
+ * Provides access to the database through a singleton-object.
+ */
+@Database(
+ entities = [Head::class, Item::class],
+ version = 1,
+ exportSchema = false
+)
+@TypeConverters(Converters::class)
+abstract class EasyBillDatabase : RoomDatabase() {
+
+ /**
+ * Getter for the HeadDao.
+ */
+ abstract fun getHeadDao(): HeadDao
+
+ /**
+ * Getter for the ItemDao.
+ */
+ abstract fun getItemDao(): ItemDao
+
+ /**
+ * Getter for the BillDao.
+ */
+ abstract fun getBillDao(): BillDao
+
+ /**
+ * Keeps the singleton.
+ */
+ companion object {
+
+ @Volatile
+ private var instance: EasyBillDatabase? = null
+
+ /**
+ * Provides thread-safe access to the EasyBillDatabase singleton-object.
+ */
+ fun getInstance(context: Context): EasyBillDatabase {
+ val tmp = instance
+ if (tmp != null)
+ return tmp
+
+ return synchronized(this) {
+ if (instance != null) {
+ instance!!
+ } else {
+ instance = Room.databaseBuilder(
+ context.applicationContext,
+ EasyBillDatabase::class.java,
+ "easyBillDatabase"
+ ).build()
+ instance!!
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/easybill/database/dao/BillDao.kt b/app/src/main/java/com/easybill/database/dao/BillDao.kt
new file mode 100644
index 0000000..7c432df
--- /dev/null
+++ b/app/src/main/java/com/easybill/database/dao/BillDao.kt
@@ -0,0 +1,20 @@
+package com.easybill.database.dao
+
+import androidx.room.Dao
+import androidx.room.Query
+import androidx.room.Transaction
+import com.easybill.database.model.Bill
+
+@Dao
+interface BillDao {
+ @Transaction
+ @Query("SELECT * FROM head WHERE id = :key")
+ fun getBillById(key: Long): Bill
+
+ @Transaction
+ @Query("SELECT * FROM head")
+ fun getAllBills(): MutableList
+
+ @Query("DELETE FROM head")
+ fun deleteAllBills()
+}
diff --git a/app/src/main/java/com/easybill/database/dao/HeadDao.kt b/app/src/main/java/com/easybill/database/dao/HeadDao.kt
new file mode 100644
index 0000000..b725319
--- /dev/null
+++ b/app/src/main/java/com/easybill/database/dao/HeadDao.kt
@@ -0,0 +1,33 @@
+package com.easybill.database.dao
+
+import androidx.room.Dao
+import androidx.room.Delete
+import androidx.room.Insert
+import androidx.room.Query
+import androidx.room.Update
+import com.easybill.database.model.Head
+
+/**
+ * Provides CRUD-Operations to the Head-Entity.
+ */
+@Dao
+interface HeadDao {
+
+ @Insert
+ fun insert(head: Head): Long
+
+ @Update
+ fun update(head: Head)
+
+ @Delete
+ fun delete(head: Head)
+
+ @Query("DELETE FROM head WHERE id = :id")
+ fun deleteById(id: Long)
+
+ @Query("SELECT * FROM head WHERE id = :id")
+ fun getById(id: Long): Head
+
+ @Query("SELECT * FROM head")
+ fun getAll(): MutableList
+}
diff --git a/app/src/main/java/com/easybill/database/dao/ItemDao.kt b/app/src/main/java/com/easybill/database/dao/ItemDao.kt
new file mode 100644
index 0000000..8cf7ab8
--- /dev/null
+++ b/app/src/main/java/com/easybill/database/dao/ItemDao.kt
@@ -0,0 +1,30 @@
+package com.easybill.database.dao
+
+import androidx.room.Dao
+import androidx.room.Delete
+import androidx.room.Insert
+import androidx.room.Query
+import androidx.room.Update
+import com.easybill.database.model.Item
+
+/**
+ * Provides CRUD-Operations to the BillItem-Entity.
+ */
+@Dao
+interface ItemDao {
+
+ @Insert
+ fun insert(item: Item): Long
+
+ @Update
+ fun update(item: Item)
+
+ @Delete
+ fun delete(item: Item)
+
+ @Query("SELECT * from item WHERE id = :id")
+ fun getById(id: Long): Item
+
+ @Query("SELECT * FROM item")
+ fun getAll(): List-
+}
diff --git a/app/src/main/java/com/easybill/database/model/Bill.kt b/app/src/main/java/com/easybill/database/model/Bill.kt
new file mode 100644
index 0000000..48d6c21
--- /dev/null
+++ b/app/src/main/java/com/easybill/database/model/Bill.kt
@@ -0,0 +1,32 @@
+package com.easybill.database.model
+
+import androidx.room.Embedded
+import androidx.room.Relation
+
+/**
+ * A Bill consists of a head and a list of items.
+ */
+data class Bill(
+ @Embedded val head: Head,
+
+ @Relation(parentColumn = "id", entityColumn = "billId")
+ val items: List
-
+) {
+ override fun toString(): String {
+ var total = 0.0
+ for (item in items) {
+ total += item.bruttoPrice()
+ }
+ var head = String.format("Store: %s\nAddress: %s\nTime: %s\nTotal: %s\n\nItems:\n",
+ head.storeName, head.address, head.time, total)
+
+ for (item in items) {
+ head +=
+ String.format("\n%s\n\tamount: %f\n\ttax: %f\n\tnetto: %f\n\tbrutto=%f\n\ttotal=%f",
+ item.name, item.amount, item.tax,
+ item.nettoPrice, item.bruttoPrice(), item.totalPrice())
+ }
+
+ return head
+ }
+}
diff --git a/app/src/main/java/com/easybill/database/model/Head.kt b/app/src/main/java/com/easybill/database/model/Head.kt
new file mode 100644
index 0000000..76d859b
--- /dev/null
+++ b/app/src/main/java/com/easybill/database/model/Head.kt
@@ -0,0 +1,31 @@
+package com.easybill.database.model
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import java.time.LocalDateTime
+
+/**
+ * A head contains the top-section of a bill, e.g. the bills meta-data.
+ */
+@Entity(tableName = "head")
+data class Head(
+
+ @PrimaryKey(autoGenerate = true)
+ var id: Long = 0L,
+
+ /**
+ * The address of the store where the bill was obtained.
+ */
+ var address: String = "",
+
+ /**
+ * The name of the store where the bill was obtained.
+ */
+ var storeName: String = "",
+
+ /**
+ * The time that the bill was printed.
+ */
+ var time: LocalDateTime = LocalDateTime.now()
+
+)
diff --git a/app/src/main/java/com/easybill/database/model/Item.kt b/app/src/main/java/com/easybill/database/model/Item.kt
new file mode 100644
index 0000000..8d12d07
--- /dev/null
+++ b/app/src/main/java/com/easybill/database/model/Item.kt
@@ -0,0 +1,50 @@
+package com.easybill.database.model
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * An Item contains the data of a single item/position included on a bill.
+ */
+@Entity(tableName = "item")
+class Item(
+
+ @PrimaryKey(autoGenerate = true)
+ var id: Long = 0L,
+
+ /**
+ * The Bill this item is associated with.
+ */
+ var billId: Long = 0L,
+
+ /**
+ * The name of the item.
+ */
+ var name: String = "",
+
+ /**
+ * The amount of the item (pcs, weight).
+ */
+ var amount: Double = 0.0,
+
+ /**
+ * The tax rate on the item.
+ */
+ var tax: Double = 0.0,
+
+ /**
+ * The price of a single item of this kind (without tax)
+ */
+ var nettoPrice: Double = 0.0
+) {
+
+ /**
+ * Calculates the brutto-price of the item, e.g. nettoPrice * (1 + tax)
+ */
+ fun bruttoPrice(): Double = nettoPrice * (1 + tax)
+
+ /**
+ * Calculates the total price of the item, e.g. amount * unit.
+ */
+ fun totalPrice(): Double = bruttoPrice() * amount
+}
diff --git a/app/src/main/java/com/easybill/fragments/DetailedBillFragment.kt b/app/src/main/java/com/easybill/fragments/DetailedBillFragment.kt
new file mode 100644
index 0000000..42f520d
--- /dev/null
+++ b/app/src/main/java/com/easybill/fragments/DetailedBillFragment.kt
@@ -0,0 +1,80 @@
+package com.easybill.fragments
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import androidx.navigation.findNavController
+import com.easybill.R
+import com.easybill.database.EasyBillDatabase
+import com.easybill.database.model.Bill
+import com.easybill.databinding.DetailedBillBinding
+import com.easybill.viewmodel.EasyBillViewModel
+import com.easybill.viewmodel.EasyBillViewModelFactory
+
+/**
+ * Shows a bill with all its information.
+ */
+class DetailedBillFragment : Fragment() {
+
+ // keeps state when the activity gets re-loaded on device configuration change
+ private lateinit var viewModel: EasyBillViewModel
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // get bill-dao and create view-model
+ val application = activity?.application
+ if (application != null) {
+ val headDao = EasyBillDatabase.getInstance(application).getHeadDao()
+ val itemDao = EasyBillDatabase.getInstance(application).getItemDao()
+ val billDao = EasyBillDatabase.getInstance(application).getBillDao()
+
+ // create view-model
+ val viewModelFactory = EasyBillViewModelFactory(headDao, itemDao, billDao, application)
+ viewModel = ViewModelProvider(activity!!, viewModelFactory)
+ .get(EasyBillViewModel::class.java)
+ }
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+
+ // binding
+ val binding: DetailedBillBinding =
+ DataBindingUtil.inflate(inflater, R.layout.detailed_bill, container, false)
+
+ // get bill-id
+ val billId = DetailedBillFragmentArgs.fromBundle(requireArguments()).billID
+
+ // find correct bill
+ var bill: Bill? = null
+ if (viewModel.bills.value != null)
+ bill = viewModel.bills.value!!.find { it.head.id == billId }
+
+ if (bill != null) {
+
+ /*
+ * Delete bill
+ */
+ binding.datailedBillDeleteButton.setOnClickListener {
+ viewModel.deleteBillById(billId)
+ it.findNavController().navigate(R.id.action_detailedBillFragment_to_archiveFragment)
+ }
+
+ /*
+ * Set details
+ */
+ binding.detailedBillTitle.text = bill.head.storeName
+ binding.detailedBillOutput.text = bill.toString()
+ }
+
+ return binding.root
+ }
+}
diff --git a/app/src/main/java/com/easybill/fragments/FilterFragment.kt b/app/src/main/java/com/easybill/fragments/FilterFragment.kt
new file mode 100644
index 0000000..d6cba08
--- /dev/null
+++ b/app/src/main/java/com/easybill/fragments/FilterFragment.kt
@@ -0,0 +1,34 @@
+package com.easybill.fragments
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import androidx.navigation.findNavController
+import com.easybill.R
+import com.easybill.databinding.FilterBinding
+
+/**
+ * Lets the user set filters to find a subset of bills.
+ */
+class FilterFragment : Fragment() {
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+
+ val binding: FilterBinding = DataBindingUtil.inflate(
+ inflater, R.layout.filter, container, false)
+
+ binding.applyButton.setOnClickListener {
+ // TODO check if everything is right
+ it.findNavController().navigate(R.id.action_filterFragment_to_archiveFragment)
+ }
+
+ return binding.root
+ }
+}
diff --git a/app/src/main/java/com/easybill/fragments/ScanFragment.kt b/app/src/main/java/com/easybill/fragments/ScanFragment.kt
new file mode 100644
index 0000000..2517bb9
--- /dev/null
+++ b/app/src/main/java/com/easybill/fragments/ScanFragment.kt
@@ -0,0 +1,93 @@
+package com.easybill.fragments
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import androidx.navigation.findNavController
+import com.easybill.R
+import com.easybill.database.Converters
+import com.easybill.database.EasyBillDatabase
+import com.easybill.database.model.Bill
+import com.easybill.database.model.Head
+import com.easybill.database.model.Item
+import com.easybill.databinding.ScanBinding
+import com.easybill.viewmodel.EasyBillViewModel
+import com.easybill.viewmodel.EasyBillViewModelFactory
+
+/**
+ * Let's the user scan/make a photo of a new bill.
+ */
+class ScanFragment : Fragment() {
+
+ // keeps state when the activity gets re-loaded on device configuration change
+ private lateinit var viewModel: EasyBillViewModel
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // get bill-dao and create view-model
+ val application = activity?.application
+ if (application != null) {
+ val headDao = EasyBillDatabase.getInstance(application).getHeadDao()
+ val itemDao = EasyBillDatabase.getInstance(application).getItemDao()
+ val billDao = EasyBillDatabase.getInstance(application).getBillDao()
+
+ // create view-model
+ val viewModelFactory = EasyBillViewModelFactory(headDao, itemDao, billDao, application)
+ viewModel = ViewModelProvider(activity!!, viewModelFactory)
+ .get(EasyBillViewModel::class.java)
+ }
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+
+ val binding: ScanBinding = DataBindingUtil.inflate(
+ inflater, R.layout.scan, container, false
+ )
+
+ binding.scanButton.setOnClickListener {
+ // TODO scan bill and add to database
+
+ // create new mock-bill
+ val millisPerDay = 24 * 60 * 60 * 1000
+ val converter = Converters()
+ /*
+ * Bill#1
+ */
+ val headOne = Head()
+ headOne.storeName = "Media Markt"
+ headOne.address = "Fakestreet 1234, 80801 München"
+ headOne.time =
+ converter.localDateTimeFromTimestamp(System.currentTimeMillis() - millisPerDay)!!
+ // items #1.1
+ val itemOneOne = Item()
+ itemOneOne.amount = 1.0
+ itemOneOne.tax = 0.19
+ itemOneOne.name = "USB-Stick erster Güte"
+ itemOneOne.nettoPrice = 13.37
+ // items #1.2
+ val itemOneTwo = Item()
+ itemOneTwo.amount = 3.0
+ itemOneTwo.tax = 0.19
+ itemOneTwo.name = "Wirklich schnelle SSD"
+ itemOneTwo.nettoPrice = 199.00
+ val billOne = Bill(
+ headOne,
+ listOf(itemOneOne, itemOneTwo)
+ )
+ viewModel.addBill(billOne)
+
+ it.findNavController().navigate(R.id.action_scanFragment_to_archiveFragment)
+ }
+
+ return binding.root
+ }
+}
diff --git a/app/src/main/java/com/easybill/fragments/StatisticsFragment.kt b/app/src/main/java/com/easybill/fragments/StatisticsFragment.kt
new file mode 100644
index 0000000..1171fae
--- /dev/null
+++ b/app/src/main/java/com/easybill/fragments/StatisticsFragment.kt
@@ -0,0 +1,28 @@
+package com.easybill.fragments
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import com.easybill.R
+import com.easybill.databinding.StatisticBinding
+
+/**
+ * Shows statistics of all bills.
+ */
+class StatisticsFragment : Fragment() {
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+
+ val binding: StatisticBinding = DataBindingUtil.inflate(
+ inflater, R.layout.statistic, container, false)
+
+ return binding.root
+ }
+}
diff --git a/app/src/main/java/com/easybill/fragments/archive/ArchiveAdapter.kt b/app/src/main/java/com/easybill/fragments/archive/ArchiveAdapter.kt
new file mode 100644
index 0000000..0c21546
--- /dev/null
+++ b/app/src/main/java/com/easybill/fragments/archive/ArchiveAdapter.kt
@@ -0,0 +1,53 @@
+package com.easybill.fragments.archive
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.navigation.findNavController
+import androidx.recyclerview.widget.RecyclerView
+import com.easybill.R
+import com.easybill.database.model.Bill
+import com.easybill.viewmodel.EasyBillViewModel
+import kotlinx.android.synthetic.main.archive_listview_item.view.*
+
+class ArchiveAdapter(private var viewModel: EasyBillViewModel) :
+ RecyclerView.Adapter() {
+
+ inner class BillsWithItemsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ fun bind(bill: Bill) {
+ itemView.list_view_company_name.text = bill.head.storeName
+ itemView.list_view_date.text = bill.head.time.toString()
+
+ var total = 0.0
+ for (item in bill.items) {
+ total += item.nettoPrice * item.amount
+ }
+ itemView.list_view_price.text = "$total €"
+
+ itemView.setOnClickListener {
+ it.findNavController().navigate(ArchiveFragmentDirections
+ .actionArchiveFragmentToDetailedBillFragment(bill.head.id))
+ }
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BillsWithItemsViewHolder {
+ val itemView =
+ LayoutInflater.from(parent.context)
+ .inflate(R.layout.archive_listview_item, parent, false)
+
+ return BillsWithItemsViewHolder(itemView)
+ }
+
+ override fun getItemCount(): Int {
+ // Timber.i("item count is %d (%s)", viewModel.bills.value?.size?: 0, viewModel.bills.value)
+ return viewModel.bills.value?.size ?: 0
+ }
+
+ override fun onBindViewHolder(holder: BillsWithItemsViewHolder, position: Int) {
+ val tmp = viewModel.bills.value?.get(position)
+ // Timber.i("tmp is %s", tmp)
+ if (tmp != null)
+ holder.bind(tmp)
+ }
+}
diff --git a/app/src/main/java/com/easybill/fragments/archive/ArchiveFragment.kt b/app/src/main/java/com/easybill/fragments/archive/ArchiveFragment.kt
new file mode 100644
index 0000000..9f61040
--- /dev/null
+++ b/app/src/main/java/com/easybill/fragments/archive/ArchiveFragment.kt
@@ -0,0 +1,136 @@
+package com.easybill.fragments.archive
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Toast
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
+import androidx.navigation.findNavController
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.easybill.R
+import com.easybill.database.EasyBillDatabase
+import com.easybill.databinding.ArchiveBinding
+import com.easybill.viewmodel.EasyBillViewModel
+import com.easybill.viewmodel.EasyBillViewModelFactory
+
+/**
+ * Displays the bill-archive. This is the first fragment that is shown to the user
+ * when the application is started.
+ */
+class ArchiveFragment : Fragment() {
+
+ // data-binding for this activity
+ private lateinit var binding: ArchiveBinding
+
+ // keeps state when the activity gets re-loaded on device configuration change
+ private lateinit var viewModel: EasyBillViewModel
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ binding = DataBindingUtil.inflate(
+ layoutInflater, R.layout.archive, null, false)
+
+ // get bill-dao and create view-model
+ val application = activity?.application
+ if (application != null) {
+ val headDao = EasyBillDatabase.getInstance(application).getHeadDao()
+ val itemDao = EasyBillDatabase.getInstance(application).getItemDao()
+ val billDao = EasyBillDatabase.getInstance(application).getBillDao()
+
+ // create view-model
+ val viewModelFactory = EasyBillViewModelFactory(headDao, itemDao, billDao, application)
+ viewModel = ViewModelProvider(activity!!, viewModelFactory)
+ .get(EasyBillViewModel::class.java)
+ }
+
+ // set bindings view-model
+ binding.billViewModel = viewModel
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+
+ // setup recycler-view
+ // activity?.findViewById(R.id.archiveRecyclerView)
+ binding.archiveRecyclerView.layoutManager = LinearLayoutManager(context)
+ viewModel.bills.observe(viewLifecycleOwner, Observer {
+ binding.archiveRecyclerView.adapter =
+ ArchiveAdapter(viewModel)
+ })
+
+ viewModel.getAllBills()
+
+ /*
+ * Navigate to ScanFragment
+ */
+ binding.buttonAdd.setOnClickListener { view: View ->
+ view.findNavController().navigate(R.id.action_archiveFragment_to_scanFragment)
+ }
+
+ /*
+ * Navigate to FilterFragment
+ */
+ binding.buttonFilter.setOnClickListener {
+ closeButtonMenu()
+ it.findNavController().navigate(R.id.action_archiveFragment_to_filterFragment)
+ }
+
+ /*
+ * Open/close menu
+ */
+ binding.buttonMenu.setOnClickListener {
+ onMenuButtonClicked()
+ }
+
+ /*
+ * Sort by date
+ */
+ binding.buttonSortByDate.setOnClickListener {
+ // TODO: get bills from database sorted by date
+ closeButtonMenu()
+ Toast.makeText(this.activity, "Sorted by date", Toast.LENGTH_LONG).show()
+ }
+
+ /*
+ * Sort by price
+ */
+ binding.buttonSortByPrice.setOnClickListener {
+ // TODO: get bills from database sorted by price
+ closeButtonMenu()
+ Toast.makeText(this.activity, "Sorted by price", Toast.LENGTH_LONG).show()
+ }
+
+ /*
+ * Navigate to statistics
+ */
+ binding.buttonStatistics.setOnClickListener {
+ closeButtonMenu()
+ it.findNavController().navigate(R.id.action_archiveFragment_to_statisticsFragment)
+ }
+
+ return binding.root
+ }
+
+ private fun onMenuButtonClicked() {
+ if (binding.buttonsList.visibility == View.INVISIBLE)
+ openButtonMenu()
+ else
+ closeButtonMenu()
+ }
+
+ private fun openButtonMenu() {
+ binding.buttonsList.visibility = View.VISIBLE
+ }
+
+ private fun closeButtonMenu() {
+ binding.buttonsList.visibility = View.INVISIBLE
+ }
+}
diff --git a/app/src/main/java/com/easybill/viewmodel/EasyBillViewModel.kt b/app/src/main/java/com/easybill/viewmodel/EasyBillViewModel.kt
new file mode 100644
index 0000000..bee2459
--- /dev/null
+++ b/app/src/main/java/com/easybill/viewmodel/EasyBillViewModel.kt
@@ -0,0 +1,98 @@
+package com.easybill.viewmodel
+
+import android.app.Application
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import com.easybill.database.dao.BillDao
+import com.easybill.database.dao.HeadDao
+import com.easybill.database.dao.ItemDao
+import com.easybill.database.model.Bill
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+class EasyBillViewModel(
+ private val headDao: HeadDao,
+ private val itemDao: ItemDao,
+ private val billDao: BillDao,
+ application: Application
+) : AndroidViewModel(application) {
+
+ // scope & job for coroutines
+ private var job: Job = Job()
+ private var uiScope: CoroutineScope = CoroutineScope(Dispatchers.Main + job)
+
+ // keeps/caches all bills
+ private var privBills = MutableLiveData>()
+ val bills: LiveData> get() = privBills
+
+ init {
+ privBills.value = mutableListOf()
+ getAllBills() // get all bills from database to populate cache
+ }
+
+ /**
+ * Add a bill.
+ */
+ fun addBill(bill: Bill) = uiScope.launch {
+ addBillWithItemsToDatabase(bill)
+ }
+
+ private suspend fun addBillWithItemsToDatabase(bill: Bill) =
+ withContext(Dispatchers.IO) {
+ bill.head.id = headDao.insert(bill.head)
+
+ for (item in bill.items) {
+ item.billId = bill.head.id
+ item.id = itemDao.insert(item)
+ }
+
+ if (!privBills.value!!.any { b -> b.head.id == bill.head.id }) {
+ privBills.value?.add(bill)
+ }
+ }
+
+ /**
+ * Get all bills.
+ */
+ fun getAllBills() = uiScope.launch {
+ suspendGetAllBills()
+ }
+
+ private suspend fun suspendGetAllBills() = withContext(Dispatchers.IO) {
+ val allBills = billDao.getAllBills()
+
+ for (bill in allBills) {
+ if (!privBills.value!!.any { b -> b.head.id == bill.head.id }) {
+ privBills.value!!.add(bill)
+ }
+ }
+ }
+
+ /**
+ * Delete a Bill by Id.
+ */
+ fun deleteBillById(id: Long) = uiScope.launch {
+ suspendDeleteBillById(id)
+ }
+
+ private suspend fun suspendDeleteBillById(id: Long) = withContext(Dispatchers.IO) {
+ headDao.deleteById(id)
+ privBills.value?.removeIf { it.head.id == id }
+ }
+
+ /**
+ * Delete all Bills (head & items).
+ */
+ fun deleteAllBills() = uiScope.launch {
+ suspendedDeleteAllBills()
+ }
+
+ private suspend fun suspendedDeleteAllBills() = withContext(Dispatchers.IO) {
+ billDao.deleteAllBills()
+ privBills.value?.clear()
+ }
+}
diff --git a/app/src/main/java/com/easybill/viewmodel/EasyBillViewModelFactory.kt b/app/src/main/java/com/easybill/viewmodel/EasyBillViewModelFactory.kt
new file mode 100644
index 0000000..22c19f7
--- /dev/null
+++ b/app/src/main/java/com/easybill/viewmodel/EasyBillViewModelFactory.kt
@@ -0,0 +1,25 @@
+package com.easybill.viewmodel
+
+import android.app.Application
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.easybill.database.dao.BillDao
+import com.easybill.database.dao.HeadDao
+import com.easybill.database.dao.ItemDao
+import java.lang.IllegalArgumentException
+
+@Suppress("UNCHECKED_CAST")
+class EasyBillViewModelFactory(
+ private val headDao: HeadDao,
+ private val itemDao: ItemDao,
+ private val billDao: BillDao,
+ private val application: Application
+) : ViewModelProvider.Factory {
+
+ override fun create(modelClass: Class): T {
+ if (modelClass.isAssignableFrom(EasyBillViewModel::class.java))
+ return EasyBillViewModel(headDao, itemDao, billDao, application) as T
+
+ throw IllegalArgumentException("Unknown ViewModel class")
+ }
+}
diff --git a/app/src/main/java/com/example/easybill/MainActivity.kt b/app/src/main/java/com/example/easybill/MainActivity.kt
deleted file mode 100644
index 58a1ddf..0000000
--- a/app/src/main/java/com/example/easybill/MainActivity.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.example.easybill
-
-import androidx.appcompat.app.AppCompatActivity
-import android.os.Bundle
-
-class MainActivity : AppCompatActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
- }
-}
diff --git a/app/src/main/res/drawable-anydpi/ic_add.xml b/app/src/main/res/drawable-anydpi/ic_add.xml
new file mode 100644
index 0000000..fa79705
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_add.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/drawable-anydpi/ic_calendar.xml b/app/src/main/res/drawable-anydpi/ic_calendar.xml
new file mode 100644
index 0000000..85ae787
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_calendar.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable-anydpi/ic_filter.xml b/app/src/main/res/drawable-anydpi/ic_filter.xml
new file mode 100644
index 0000000..2265cbd
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_filter.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable-anydpi/ic_menu.xml b/app/src/main/res/drawable-anydpi/ic_menu.xml
new file mode 100644
index 0000000..28b7856
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_menu.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/drawable-anydpi/ic_money.xml b/app/src/main/res/drawable-anydpi/ic_money.xml
new file mode 100644
index 0000000..b23475a
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_money.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable-anydpi/ic_statistic.xml b/app/src/main/res/drawable-anydpi/ic_statistic.xml
new file mode 100644
index 0000000..0a50d70
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_statistic.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable-hdpi/ic_add.png b/app/src/main/res/drawable-hdpi/ic_add.png
new file mode 100644
index 0000000..2b125f5
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_add.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_calendar.png b/app/src/main/res/drawable-hdpi/ic_calendar.png
new file mode 100644
index 0000000..7f946ea
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_calendar.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_menu.png b/app/src/main/res/drawable-hdpi/ic_menu.png
new file mode 100644
index 0000000..beeda13
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_menu.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_money.png b/app/src/main/res/drawable-hdpi/ic_money.png
new file mode 100644
index 0000000..7dcb26d
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_money.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_statistic.png b/app/src/main/res/drawable-hdpi/ic_statistic.png
new file mode 100644
index 0000000..0c7f4bc
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_statistic.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_add.png b/app/src/main/res/drawable-mdpi/ic_add.png
new file mode 100644
index 0000000..a69d739
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_add.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_calendar.png b/app/src/main/res/drawable-mdpi/ic_calendar.png
new file mode 100644
index 0000000..c388a9a
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_calendar.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_menu.png b/app/src/main/res/drawable-mdpi/ic_menu.png
new file mode 100644
index 0000000..36978ca
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_menu.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_money.png b/app/src/main/res/drawable-mdpi/ic_money.png
new file mode 100644
index 0000000..ba214f7
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_money.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_statistic.png b/app/src/main/res/drawable-mdpi/ic_statistic.png
new file mode 100644
index 0000000..fe82985
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_statistic.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_add.png b/app/src/main/res/drawable-xhdpi/ic_add.png
new file mode 100644
index 0000000..4423088
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_add.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_calendar.png b/app/src/main/res/drawable-xhdpi/ic_calendar.png
new file mode 100644
index 0000000..3db9cd6
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_calendar.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_filter.png b/app/src/main/res/drawable-xhdpi/ic_filter.png
new file mode 100644
index 0000000..daa1ba6
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_filter.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_menu.png b/app/src/main/res/drawable-xhdpi/ic_menu.png
new file mode 100644
index 0000000..f25b18e
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_menu.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_money.png b/app/src/main/res/drawable-xhdpi/ic_money.png
new file mode 100644
index 0000000..69e87c0
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_money.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_statistic.png b/app/src/main/res/drawable-xhdpi/ic_statistic.png
new file mode 100644
index 0000000..0aeebce
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_statistic.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_add.png b/app/src/main/res/drawable-xxhdpi/ic_add.png
new file mode 100644
index 0000000..94b04c8
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_add.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_calendar.png b/app/src/main/res/drawable-xxhdpi/ic_calendar.png
new file mode 100644
index 0000000..d3b6b40
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_calendar.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_menu.png b/app/src/main/res/drawable-xxhdpi/ic_menu.png
new file mode 100644
index 0000000..4716e10
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_menu.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_money.png b/app/src/main/res/drawable-xxhdpi/ic_money.png
new file mode 100644
index 0000000..c8101dc
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_money.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_statistic.png b/app/src/main/res/drawable-xxhdpi/ic_statistic.png
new file mode 100644
index 0000000..4520ca4
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_statistic.png differ
diff --git a/app/src/main/res/drawable/buttons_background.xml b/app/src/main/res/drawable/buttons_background.xml
new file mode 100644
index 0000000..f900259
--- /dev/null
+++ b/app/src/main/res/drawable/buttons_background.xml
@@ -0,0 +1,9 @@
+
+
+
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_shadow_button.xml b/app/src/main/res/drawable/shape_shadow_button.xml
new file mode 100644
index 0000000..7dc80bb
--- /dev/null
+++ b/app/src/main/res/drawable/shape_shadow_button.xml
@@ -0,0 +1,19 @@
+
+
+ -
+
+
-
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 4fc2444..23c0eb2 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -6,13 +6,12 @@
android:layout_height="match_parent"
tools:context=".MainActivity">
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/archive.xml b/app/src/main/res/layout/archive.xml
new file mode 100644
index 0000000..9d18cae
--- /dev/null
+++ b/app/src/main/res/layout/archive.xml
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/archive_listview_item.xml b/app/src/main/res/layout/archive_listview_item.xml
new file mode 100644
index 0000000..45502cc
--- /dev/null
+++ b/app/src/main/res/layout/archive_listview_item.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/detailed_bill.xml b/app/src/main/res/layout/detailed_bill.xml
new file mode 100644
index 0000000..0d51a4d
--- /dev/null
+++ b/app/src/main/res/layout/detailed_bill.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/filter.xml b/app/src/main/res/layout/filter.xml
new file mode 100644
index 0000000..809d937
--- /dev/null
+++ b/app/src/main/res/layout/filter.xml
@@ -0,0 +1,337 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/scan.xml b/app/src/main/res/layout/scan.xml
new file mode 100644
index 0000000..eef942d
--- /dev/null
+++ b/app/src/main/res/layout/scan.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/statistic.xml b/app/src/main/res/layout/statistic.xml
new file mode 100644
index 0000000..fc12fae
--- /dev/null
+++ b/app/src/main/res/layout/statistic.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/statistic_detailed_view.xml b/app/src/main/res/layout/statistic_detailed_view.xml
new file mode 100644
index 0000000..e5d7d28
--- /dev/null
+++ b/app/src/main/res/layout/statistic_detailed_view.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/statistic_listview_item.xml b/app/src/main/res/layout/statistic_listview_item.xml
new file mode 100644
index 0000000..1845f65
--- /dev/null
+++ b/app/src/main/res/layout/statistic_listview_item.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/statistic_listview_month_overview.xml b/app/src/main/res/layout/statistic_listview_month_overview.xml
new file mode 100644
index 0000000..2a5a186
--- /dev/null
+++ b/app/src/main/res/layout/statistic_listview_month_overview.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
index eca70cf..036d09b 100644
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -1,5 +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
index eca70cf..036d09b 100644
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -1,5 +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
index a571e60..79ee47e 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..a0e440e
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.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
index 61da551..88963f6 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png 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
index c41dd28..afbb9ec 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..c684fa2
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.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
index db5080a..6d97206 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png 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
index 6dba46d..a52ac80 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..8c2d672
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.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
index da31a87..87d4e56 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png 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
index 15ac681..9778012 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..3f93eb7
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.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
index b216f2d..1b54efa 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png 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
index f25a419..828980a 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..77e0ac7
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.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
index e96783c..cb45760 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/navigation/navigation.xml b/app/src/main/res/navigation/navigation.xml
new file mode 100644
index 0000000..3d68a30
--- /dev/null
+++ b/app/src/main/res/navigation/navigation.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 030098f..f27b405 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -1,6 +1,12 @@
- #6200EE
- #3700B3
+ #4BB8F5
+ #0081CA
#03DAC5
+
+ #E5E5E5
+ #FFFFFF
+ #000000
+ #4BB8F5
+ #e6e6ea
diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml
new file mode 100644
index 0000000..c5d5899
--- /dev/null
+++ b/app/src/main/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+
+
+ #FFFFFF
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b26e80f..51d6a4f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,26 @@
EasyBill
+ ARCHIVE
+ PLACEHOLDER
+ SCAN
+ STATISTIC
+ DELETE
+ SCAN
+ APPLY
+ FILTER
+ PRICE:
+ TIME FROM:
+ TIME TO:
+ YEAR FROM:
+ YEAR TO:
+ CATEGORY
+ FASHION
+ FOOD
+ SPORT
+ HEALTH
+ TRAFFIC
+ ELECTRONICS
+ LIVING
+ OTHER
+ %1$d €
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 5885930..d7ed94c 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -8,4 +8,10 @@
- @color/colorAccent
+
+
diff --git a/app/src/test/java/com/example/easybill/ExampleUnitTest.kt b/app/src/test/java/com/easybill/ExampleUnitTest.kt
similarity index 91%
rename from app/src/test/java/com/example/easybill/ExampleUnitTest.kt
rename to app/src/test/java/com/easybill/ExampleUnitTest.kt
index 3b06aad..b9b6df6 100644
--- a/app/src/test/java/com/example/easybill/ExampleUnitTest.kt
+++ b/app/src/test/java/com/easybill/ExampleUnitTest.kt
@@ -1,4 +1,4 @@
-package com.example.easybill
+package com.easybill
import org.junit.Test
diff --git a/build.gradle b/build.gradle
index eb87dba..9fae599 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,21 +1,28 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext.kotlin_version = '1.3.61'
+ ext.kotlin_version = '1.3.72'
+ ext.navigationVersion = '1.0.0-rc02'
repositories {
google()
jcenter()
-
+
+ maven{
+ url "https://plugins.gradle.org/m2/"
+ }
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.6.1'
+ classpath 'com.android.tools.build:gradle:3.6.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
-
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
+ classpath "org.jlleitschuh.gradle:ktlint-gradle:9.2.1"
+ classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
}
}
+plugins {
+ id "org.jlleitschuh.gradle.ktlint" version "9.2.1"
+}
+
allprojects {
repositories {
google()
@@ -26,4 +33,4 @@ allprojects {
task clean(type: Delete) {
delete rootProject.buildDir
-}
+}
\ No newline at end of file
diff --git a/digital-bill-highres (1).png b/digital-bill-highres (1).png
deleted file mode 100644
index cd9a051..0000000
Binary files a/digital-bill-highres (1).png and /dev/null differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index d30cec5..03e4089 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,10 +1,6 @@
-<<<<<<< HEAD
-#Fri May 15 11:38:44 CEST 2020
-=======
-#Fri May 15 11:29:46 CEST 2020
->>>>>>> 6164b7ab9283971583ecc0a415a805140fc8b571
+#Tue Jun 02 17:04:00 CEST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
\ No newline at end of file
diff --git a/gradlew b/gradlew
old mode 100644
new mode 100755
diff --git a/local.properties b/local.properties
new file mode 100644
index 0000000..7695bde
--- /dev/null
+++ b/local.properties
@@ -0,0 +1,8 @@
+## This file must *NOT* be checked into Version Control Systems,
+# as it contains information specific to your local configuration.
+#
+# Location of the SDK. This is only used by Gradle.
+# For customization when using a Version Control System, please read the
+# header note.
+#Thu Jun 04 17:25:31 CEST 2020
+sdk.dir=C\:\\Users\\Thomas.DESKTOP-OQNA4AK\\AppData\\Local\\Android\\Sdk
diff --git a/res_img/1.PNG b/res_img/1.PNG
new file mode 100644
index 0000000..44b75c2
Binary files /dev/null and b/res_img/1.PNG differ
diff --git a/res_img/2.PNG b/res_img/2.PNG
new file mode 100644
index 0000000..a339a39
Binary files /dev/null and b/res_img/2.PNG differ
diff --git a/res_img/3.PNG b/res_img/3.PNG
new file mode 100644
index 0000000..c9e9da2
Binary files /dev/null and b/res_img/3.PNG differ
diff --git a/res_img/4.PNG b/res_img/4.PNG
new file mode 100644
index 0000000..ba5f06a
Binary files /dev/null and b/res_img/4.PNG differ
diff --git a/res_img/5.PNG b/res_img/5.PNG
new file mode 100644
index 0000000..72682fa
Binary files /dev/null and b/res_img/5.PNG differ
diff --git a/res_img/digital-bill-highres.png b/res_img/digital-bill-highres.png
new file mode 100644
index 0000000..a67cf6f
Binary files /dev/null and b/res_img/digital-bill-highres.png differ