Add categories to transactions
This commit is contained in:
parent
9cd1428db8
commit
85a91d0af0
40 changed files with 885 additions and 97 deletions
17
.project
Normal file
17
.project
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>MyAllowance</name>
|
||||||
|
<comment>Project MyAllowance created by Buildship.</comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
2
.settings/org.eclipse.buildship.core.prefs
Normal file
2
.settings/org.eclipse.buildship.core.prefs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
connection.project.dir=
|
||||||
|
eclipse.preferences.version=1
|
6
app/.classpath
Normal file
6
app/.classpath
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||||
|
<classpathentry kind="output" path="bin/default"/>
|
||||||
|
</classpath>
|
23
app/.project
Normal file
23
app/.project
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>app</name>
|
||||||
|
<comment>Project app created by Buildship.</comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
2
app/.settings/org.eclipse.buildship.core.prefs
Normal file
2
app/.settings/org.eclipse.buildship.core.prefs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
connection.project.dir=..
|
||||||
|
eclipse.preferences.version=1
|
|
@ -18,7 +18,7 @@ android {
|
||||||
}
|
}
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.wbrawner.budget"
|
applicationId "com.wbrawner.budget"
|
||||||
minSdkVersion 23
|
minSdkVersion 24
|
||||||
targetSdkVersion 27
|
targetSdkVersion 27
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
|
@ -29,6 +29,11 @@ android {
|
||||||
buildConfigField "String", "ACRA_URL", "\"${acra.getProperty("url")}\""
|
buildConfigField "String", "ACRA_URL", "\"${acra.getProperty("url")}\""
|
||||||
buildConfigField "String", "ACRA_USER", "\"${acra.getProperty("user")}\""
|
buildConfigField "String", "ACRA_USER", "\"${acra.getProperty("user")}\""
|
||||||
buildConfigField "String", "ACRA_PASS", "\"${acra.getProperty("pass")}\""
|
buildConfigField "String", "ACRA_PASS", "\"${acra.getProperty("pass")}\""
|
||||||
|
javaCompileOptions {
|
||||||
|
annotationProcessorOptions {
|
||||||
|
arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
|
@ -36,6 +41,9 @@ android {
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sourceSets {
|
||||||
|
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
113
app/schemas/com.wbrawner.budget.data.BudgetDatabase/2.json
Normal file
113
app/schemas/com.wbrawner.budget.data.BudgetDatabase/2.json
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 2,
|
||||||
|
"identityHash": "b68fd456557e5153b2fe6514a5193c07",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "Transaction",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER, `title` TEXT NOT NULL, `date` TEXT NOT NULL, `description` TEXT NOT NULL, `amount` REAL NOT NULL, `categoryId` INTEGER, `type` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "date",
|
||||||
|
"columnName": "date",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "description",
|
||||||
|
"columnName": "description",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "amount",
|
||||||
|
"columnName": "amount",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "categoryId",
|
||||||
|
"columnName": "categoryId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Category",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER, `name` TEXT NOT NULL, `amount` REAL NOT NULL, `repeat` TEXT, `color` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "amount",
|
||||||
|
"columnName": "amount",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "repeat",
|
||||||
|
"columnName": "repeat",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "color",
|
||||||
|
"columnName": "color",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"b68fd456557e5153b2fe6514a5193c07\")"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,8 @@
|
||||||
android:name="android.app.shortcuts"
|
android:name="android.app.shortcuts"
|
||||||
android:resource="@xml/shortcuts" />
|
android:resource="@xml/shortcuts" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name="com.wbrawner.budget.addedittransaction.AddEditTransactionActivity" />
|
<activity android:name="com.wbrawner.budget.transactions.AddEditTransactionActivity" />
|
||||||
|
<activity android:name="com.wbrawner.budget.categories.AddEditCategoryActivity" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
|
@ -3,9 +3,10 @@ package com.wbrawner.budget
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.arch.persistence.room.Room
|
import android.arch.persistence.room.Room
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.wbrawner.budget.data.TransactionDao
|
import com.wbrawner.budget.data.dao.TransactionDao
|
||||||
import com.wbrawner.budget.data.TransactionsDatabase
|
import com.wbrawner.budget.data.BudgetDatabase
|
||||||
import com.wbrawner.budget.BuildConfig
|
import com.wbrawner.budget.data.dao.CategoryDao
|
||||||
|
import com.wbrawner.budget.data.migrations.MIGRATION_1_2
|
||||||
import org.acra.ACRA
|
import org.acra.ACRA
|
||||||
import org.acra.annotation.AcraCore
|
import org.acra.annotation.AcraCore
|
||||||
import org.acra.annotation.AcraHttpSender
|
import org.acra.annotation.AcraHttpSender
|
||||||
|
@ -18,23 +19,28 @@ import org.acra.sender.HttpSender
|
||||||
basicAuthPassword = BuildConfig.ACRA_PASS,
|
basicAuthPassword = BuildConfig.ACRA_PASS,
|
||||||
httpMethod = HttpSender.Method.POST)
|
httpMethod = HttpSender.Method.POST)
|
||||||
class AllowanceApplication: Application() {
|
class AllowanceApplication: Application() {
|
||||||
lateinit var database: TransactionsDatabase
|
lateinit var database: BudgetDatabase
|
||||||
private set
|
private set
|
||||||
|
|
||||||
lateinit var dao: TransactionDao
|
lateinit var transactionDao: TransactionDao
|
||||||
|
private set
|
||||||
|
|
||||||
|
lateinit var categoryDao: CategoryDao
|
||||||
private set
|
private set
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
database = Room.databaseBuilder(applicationContext, TransactionsDatabase::class.java, "transactions")
|
database = Room.databaseBuilder(applicationContext, BudgetDatabase::class.java, "transactions")
|
||||||
|
.addMigrations(MIGRATION_1_2())
|
||||||
.build()
|
.build()
|
||||||
dao = database.dao()
|
transactionDao = database.transactionDao()
|
||||||
|
categoryDao = database.categoryDao()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun attachBaseContext(base: Context?) {
|
override fun attachBaseContext(base: Context?) {
|
||||||
super.attachBaseContext(base)
|
super.attachBaseContext(base)
|
||||||
ACRA.init(this)
|
if (!BuildConfig.DEBUG) ACRA.init(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
package com.wbrawner.budget.categories
|
||||||
|
|
||||||
|
import android.arch.lifecycle.Observer
|
||||||
|
import android.arch.lifecycle.ViewModelProviders
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.support.v7.app.AppCompatActivity
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuItem
|
||||||
|
import com.wbrawner.budget.R
|
||||||
|
import com.wbrawner.budget.data.model.Category
|
||||||
|
import kotlinx.android.synthetic.main.activity_add_edit_category.*
|
||||||
|
|
||||||
|
class AddEditCategoryActivity : AppCompatActivity() {
|
||||||
|
lateinit var viewModel: CategoryViewModel
|
||||||
|
var id: Int? = null
|
||||||
|
var menu: Menu? = null
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_add_edit_category)
|
||||||
|
setSupportActionBar(action_bar)
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
viewModel = ViewModelProviders.of(this).get(CategoryViewModel::class.java)
|
||||||
|
|
||||||
|
if (intent?.hasExtra(EXTRA_CATEGORY_ID) == false) {
|
||||||
|
setTitle(R.string.title_add_category)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.getCategory(intent!!.extras!!.getInt(EXTRA_CATEGORY_ID))
|
||||||
|
.observe(this, Observer<Category> { category ->
|
||||||
|
if (category == null) {
|
||||||
|
menu?.findItem(R.id.action_delete)?.isVisible = false
|
||||||
|
return@Observer
|
||||||
|
}
|
||||||
|
id = category.id
|
||||||
|
setTitle(R.string.title_edit_category)
|
||||||
|
menu?.findItem(R.id.action_delete)?.isVisible = true
|
||||||
|
edit_category_name.setText(category.name)
|
||||||
|
edit_category_amount.setText(String.format("%.02f", category.amount))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||||
|
menuInflater.inflate(R.menu.menu_add_edit, menu)
|
||||||
|
if (id != null) {
|
||||||
|
menu?.findItem(R.id.action_delete)?.isVisible = true
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
||||||
|
when (item?.itemId) {
|
||||||
|
android.R.id.home -> onBackPressed()
|
||||||
|
R.id.action_save -> {
|
||||||
|
if (!validateFields()) return true
|
||||||
|
viewModel.saveCategory(Category(
|
||||||
|
id = id,
|
||||||
|
name = edit_category_name.text.toString(),
|
||||||
|
amount = edit_category_amount.text.toString().toDouble(),
|
||||||
|
color = Color.parseColor("#FF0000"),
|
||||||
|
repeat = "never"
|
||||||
|
))
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
R.id.action_delete -> {
|
||||||
|
viewModel.deleteCategoryById(this@AddEditCategoryActivity.id!!)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun validateFields(): Boolean {
|
||||||
|
var errors = false
|
||||||
|
if (edit_category_name.text.isEmpty()) {
|
||||||
|
edit_category_name.error = getString(R.string.required_field_name)
|
||||||
|
errors = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (edit_category_amount.text.isEmpty()) {
|
||||||
|
edit_category_amount.error = getString(R.string.required_field_amount)
|
||||||
|
errors = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return !errors
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val EXTRA_CATEGORY_ID = "EXTRA_CATEGORY_ID"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package com.wbrawner.budget.categories
|
||||||
|
|
||||||
|
import android.arch.lifecycle.LifecycleOwner
|
||||||
|
import android.arch.lifecycle.Observer
|
||||||
|
import android.content.Intent
|
||||||
|
import android.support.v4.content.ContextCompat.startActivity
|
||||||
|
import android.support.v7.widget.RecyclerView
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ProgressBar
|
||||||
|
import android.widget.TextView
|
||||||
|
import com.wbrawner.budget.R
|
||||||
|
import com.wbrawner.budget.categories.AddEditCategoryActivity.Companion.EXTRA_CATEGORY_ID
|
||||||
|
import com.wbrawner.budget.data.model.Category
|
||||||
|
|
||||||
|
class CategoryAdapter(
|
||||||
|
private val lifecycleOwner: LifecycleOwner,
|
||||||
|
private val data: List<Category>,
|
||||||
|
private val viewModel: CategoryViewModel
|
||||||
|
) : RecyclerView.Adapter<CategoryAdapter.ViewHolder>() {
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
val view = LayoutInflater.from(parent.context)
|
||||||
|
.inflate(R.layout.list_item_category, parent, false)
|
||||||
|
return ViewHolder(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = data.size
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
val category = data[position]
|
||||||
|
holder.title?.text = category.name
|
||||||
|
holder.amount?.text = String.format("${'$'}%.02f", category.amount)
|
||||||
|
viewModel.getCurrentBalance(category.id!!)
|
||||||
|
.observe(lifecycleOwner, Observer<Double> { balance ->
|
||||||
|
holder.progress?.isIndeterminate = false
|
||||||
|
if (balance == null) {
|
||||||
|
holder.progress?.progress = 0
|
||||||
|
} else {
|
||||||
|
holder.progress?.max = category.amount.toInt()
|
||||||
|
holder.progress?.setProgress(
|
||||||
|
Math.abs(balance).toInt(),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
holder.itemView.setOnClickListener {
|
||||||
|
startActivity(
|
||||||
|
it.context.applicationContext,
|
||||||
|
Intent(it.context.applicationContext, AddEditCategoryActivity::class.java)
|
||||||
|
.apply {
|
||||||
|
putExtra(EXTRA_CATEGORY_ID, category.id)
|
||||||
|
},
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
val title: TextView? = itemView.findViewById(R.id.category_title)
|
||||||
|
val amount: TextView? = itemView.findViewById(R.id.category_amount)
|
||||||
|
val progress: ProgressBar? = itemView.findViewById(R.id.category_progress)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
package com.wbrawner.budget.categories
|
||||||
|
|
||||||
|
import android.arch.lifecycle.Observer
|
||||||
|
import android.arch.lifecycle.ViewModelProviders
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.support.design.widget.FloatingActionButton
|
||||||
|
import android.support.text.emoji.widget.EmojiTextView
|
||||||
|
import android.support.v4.app.Fragment
|
||||||
|
import android.support.v7.widget.LinearLayoutManager
|
||||||
|
import android.support.v7.widget.RecyclerView
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import com.wbrawner.budget.R
|
||||||
|
import com.wbrawner.budget.data.model.Category
|
||||||
|
|
||||||
|
class CategoryListFragment : Fragment() {
|
||||||
|
lateinit var viewModel: CategoryViewModel
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
if (activity == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel = ViewModelProviders.of(activity!!).get(CategoryViewModel::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
val view = inflater.inflate(R.layout.fragment_transaction_list, container, false)
|
||||||
|
val recyclerView = view.findViewById<RecyclerView>(R.id.list_transactions)
|
||||||
|
val fab = view.findViewById<FloatingActionButton>(R.id.fab_add_transaction)
|
||||||
|
recyclerView.layoutManager = LinearLayoutManager(activity)
|
||||||
|
viewModel.getCategories()
|
||||||
|
.observe(this, Observer<List<Category>> { data ->
|
||||||
|
val noDataView = view.findViewById<EmojiTextView>(R.id.transaction_list_no_data)
|
||||||
|
if (data == null || data.isEmpty()) {
|
||||||
|
recyclerView.adapter = null
|
||||||
|
noDataView?.setText("No data FIX ME")
|
||||||
|
recyclerView?.visibility = View.GONE
|
||||||
|
noDataView?.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
recyclerView.adapter = CategoryAdapter(this, data, viewModel)
|
||||||
|
recyclerView.visibility = View.VISIBLE
|
||||||
|
noDataView.visibility = View.GONE
|
||||||
|
}
|
||||||
|
})
|
||||||
|
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
|
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
|
||||||
|
if (dy > 0) fab.hide() else fab.show()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
fab.setOnClickListener {
|
||||||
|
startActivity(Intent(activity, AddEditCategoryActivity::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG_FRAGMENT = "categories"
|
||||||
|
const val TITLE_FRAGMENT = R.string.title_categories
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.wbrawner.budget.categories
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.arch.lifecycle.AndroidViewModel
|
||||||
|
import android.arch.lifecycle.LiveData
|
||||||
|
import com.wbrawner.budget.AllowanceApplication
|
||||||
|
import com.wbrawner.budget.data.model.Category
|
||||||
|
import com.wbrawner.budget.data.CategoryRepository
|
||||||
|
|
||||||
|
class CategoryViewModel(application: Application): AndroidViewModel(application) {
|
||||||
|
private val categoryRepo = CategoryRepository((application as AllowanceApplication).categoryDao)
|
||||||
|
|
||||||
|
fun getCategory(id: Int): LiveData<Category> = categoryRepo.getCategory(id)
|
||||||
|
|
||||||
|
fun getCategories(): LiveData<List<Category>> = categoryRepo.getCategories()
|
||||||
|
|
||||||
|
fun saveCategory(category: Category) = categoryRepo.save(category)
|
||||||
|
|
||||||
|
fun deleteCategory(category: Category) = categoryRepo.delete(category)
|
||||||
|
|
||||||
|
fun deleteCategoryById(id: Int) = categoryRepo.deleteById(id)
|
||||||
|
|
||||||
|
fun getCurrentBalance(id: Int) = categoryRepo.getCurrentBalance(id)
|
||||||
|
|
||||||
|
fun getTransactions(id: Int) = categoryRepo.getTransactions(id)
|
||||||
|
}
|
16
app/src/main/java/com/wbrawner/budget/data/BudgetDatabase.kt
Normal file
16
app/src/main/java/com/wbrawner/budget/data/BudgetDatabase.kt
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package com.wbrawner.budget.data
|
||||||
|
|
||||||
|
import android.arch.persistence.room.Database
|
||||||
|
import android.arch.persistence.room.RoomDatabase
|
||||||
|
import android.arch.persistence.room.TypeConverters
|
||||||
|
import com.wbrawner.budget.data.dao.CategoryDao
|
||||||
|
import com.wbrawner.budget.data.dao.TransactionDao
|
||||||
|
import com.wbrawner.budget.data.model.Category
|
||||||
|
import com.wbrawner.budget.data.model.Transaction
|
||||||
|
|
||||||
|
@Database(entities = [(Transaction::class), (Category::class)], version = 2)
|
||||||
|
@TypeConverters(DateTypeConverter::class, TransactionTypeTypeConverter::class)
|
||||||
|
abstract class BudgetDatabase: RoomDatabase() {
|
||||||
|
abstract fun transactionDao(): TransactionDao
|
||||||
|
abstract fun categoryDao(): CategoryDao
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.wbrawner.budget.data
|
||||||
|
|
||||||
|
import android.arch.lifecycle.LiveData
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.HandlerThread
|
||||||
|
import com.wbrawner.budget.data.dao.CategoryDao
|
||||||
|
import com.wbrawner.budget.data.model.Category
|
||||||
|
import com.wbrawner.budget.data.model.Transaction
|
||||||
|
|
||||||
|
class CategoryRepository(private val dao: CategoryDao) {
|
||||||
|
private val handler: Handler
|
||||||
|
|
||||||
|
init {
|
||||||
|
val thread = HandlerThread("category")
|
||||||
|
thread.start()
|
||||||
|
handler = Handler(thread.looper)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCategories(): LiveData<List<Category>> = dao.loadMultiple()
|
||||||
|
|
||||||
|
|
||||||
|
fun getCategory(id: Int): LiveData<Category> = dao.load(id)
|
||||||
|
|
||||||
|
|
||||||
|
fun save(category: Category) {
|
||||||
|
handler.post { dao.save(category) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun delete(category: Category) {
|
||||||
|
handler.post { dao.delete(category) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun deleteById(id: Int) {
|
||||||
|
handler.post { dao.deleteById(id) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun getCurrentBalance(id: Int): LiveData<Double> = dao.getBalanceForCategory(id)
|
||||||
|
|
||||||
|
|
||||||
|
fun getTransactions(id: Int): LiveData<List<Transaction>> = dao.getTransactions(id)
|
||||||
|
}
|
|
@ -3,10 +3,13 @@ package com.wbrawner.budget.data
|
||||||
import android.arch.lifecycle.LiveData
|
import android.arch.lifecycle.LiveData
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.HandlerThread
|
import android.os.HandlerThread
|
||||||
|
import com.wbrawner.budget.data.dao.TransactionDao
|
||||||
|
import com.wbrawner.budget.data.model.Transaction
|
||||||
|
import com.wbrawner.budget.data.model.TransactionCategory
|
||||||
|
import com.wbrawner.budget.data.model.TransactionWithCategory
|
||||||
|
|
||||||
class TransactionRepository(val dao: TransactionDao) {
|
class TransactionRepository(private val dao: TransactionDao) {
|
||||||
val handler: Handler
|
private val handler: Handler
|
||||||
val uiHandler: Handler = Handler()
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val thread = HandlerThread("transactions")
|
val thread = HandlerThread("transactions")
|
||||||
|
@ -14,36 +17,13 @@ class TransactionRepository(val dao: TransactionDao) {
|
||||||
handler = Handler(thread.looper)
|
handler = Handler(thread.looper)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTransactionsByType(count: Int, type: TransactionType): LiveData<List<Transaction>> =
|
fun getTransactionsByType(count: Int, type: TransactionType): LiveData<List<TransactionWithCategory>> =
|
||||||
dao.loadMultipleByType(count, type)
|
dao.loadMultipleByType(count, type)
|
||||||
|
|
||||||
fun getTransactions(count: Int): LiveData<List<Transaction>> = dao.loadMultiple(count)
|
fun getTransactions(count: Int): LiveData<List<TransactionWithCategory>> = dao.loadMultiple(count)
|
||||||
|
|
||||||
// fun getTransactions(count: Int): LiveData<List<Transaction>> {
|
|
||||||
// val data = MutableLiveData<List<Transaction>>()
|
|
||||||
//
|
|
||||||
// handler.post {
|
|
||||||
// val transactions = ArrayList<Transaction>()
|
|
||||||
// for (i in 0..count) {
|
|
||||||
// transactions.add(Transaction(
|
|
||||||
// i,
|
|
||||||
// "Transaction $i",
|
|
||||||
// Date(),
|
|
||||||
// "Spent some money on something",
|
|
||||||
// (Math.random() * 100).toFloat(),
|
|
||||||
// TransactionType.EXPENSE
|
|
||||||
// ))
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// uiHandler.post {
|
|
||||||
// data.value = transactions
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return data
|
|
||||||
// }
|
|
||||||
|
|
||||||
fun getTransaction(id: Int): LiveData<Transaction> = dao.load(id)
|
fun getTransaction(id: Int): LiveData<TransactionWithCategory> = dao.load(id)
|
||||||
|
|
||||||
|
|
||||||
fun save(transaction: Transaction) {
|
fun save(transaction: Transaction) {
|
||||||
|
@ -62,4 +42,6 @@ class TransactionRepository(val dao: TransactionDao) {
|
||||||
|
|
||||||
|
|
||||||
fun getCurrentBalance(): LiveData<Double> = dao.getBalance()
|
fun getCurrentBalance(): LiveData<Double> = dao.getBalance()
|
||||||
|
|
||||||
|
fun getCategories(): LiveData<List<TransactionCategory>> = dao.loadCategories()
|
||||||
}
|
}
|
|
@ -1,11 +0,0 @@
|
||||||
package com.wbrawner.budget.data
|
|
||||||
|
|
||||||
import android.arch.persistence.room.Database
|
|
||||||
import android.arch.persistence.room.RoomDatabase
|
|
||||||
import android.arch.persistence.room.TypeConverters
|
|
||||||
|
|
||||||
@Database(entities = [(Transaction::class)], version = 1, exportSchema = false)
|
|
||||||
@TypeConverters(DateTypeConverter::class, TransactionTypeTypeConverter::class)
|
|
||||||
abstract class TransactionsDatabase: RoomDatabase() {
|
|
||||||
abstract fun dao(): TransactionDao
|
|
||||||
}
|
|
|
@ -1,5 +1,6 @@
|
||||||
package com.wbrawner.budget.data
|
package com.wbrawner.budget.data
|
||||||
|
|
||||||
|
import android.arch.persistence.room.TypeConverter
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.wbrawner.budget.data.dao
|
||||||
|
|
||||||
|
import android.arch.lifecycle.LiveData
|
||||||
|
import android.arch.persistence.room.Dao
|
||||||
|
import android.arch.persistence.room.Delete
|
||||||
|
import android.arch.persistence.room.Insert
|
||||||
|
import android.arch.persistence.room.OnConflictStrategy.REPLACE
|
||||||
|
import android.arch.persistence.room.Query
|
||||||
|
import com.wbrawner.budget.data.model.Category
|
||||||
|
import com.wbrawner.budget.data.model.Transaction
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface CategoryDao {
|
||||||
|
@Insert(onConflict = REPLACE)
|
||||||
|
fun save(category: Category)
|
||||||
|
|
||||||
|
@Query("SELECT * FROM `Category` WHERE id = :id")
|
||||||
|
fun load(id: Int): LiveData<Category>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM `Category`")
|
||||||
|
fun loadMultiple(): LiveData<List<Category>>
|
||||||
|
|
||||||
|
@Query("SELECT " +
|
||||||
|
"(SELECT TOTAL(amount) from `Transaction` WHERE type = 'INCOME' AND categoryId = :categoryId) " +
|
||||||
|
"- (SELECT TOTAL(amount) from `Transaction` WHERE type = 'EXPENSE' AND categoryId = :categoryId)")
|
||||||
|
fun getBalanceForCategory(categoryId: Int): LiveData<Double>
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(category: Category)
|
||||||
|
|
||||||
|
@Query("DELETE FROM `Category` WHERE id = :id")
|
||||||
|
fun deleteById(id: Int)
|
||||||
|
|
||||||
|
|
||||||
|
@Query("SELECT * FROM `Transaction` WHERE categoryId = :id")
|
||||||
|
fun getTransactions(id: Int): LiveData<List<Transaction>>
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.wbrawner.budget.data
|
package com.wbrawner.budget.data.dao
|
||||||
|
|
||||||
import android.arch.lifecycle.LiveData
|
import android.arch.lifecycle.LiveData
|
||||||
import android.arch.persistence.room.Dao
|
import android.arch.persistence.room.Dao
|
||||||
|
@ -6,6 +6,10 @@ import android.arch.persistence.room.Delete
|
||||||
import android.arch.persistence.room.Insert
|
import android.arch.persistence.room.Insert
|
||||||
import android.arch.persistence.room.OnConflictStrategy.REPLACE
|
import android.arch.persistence.room.OnConflictStrategy.REPLACE
|
||||||
import android.arch.persistence.room.Query
|
import android.arch.persistence.room.Query
|
||||||
|
import com.wbrawner.budget.data.model.Transaction
|
||||||
|
import com.wbrawner.budget.data.model.TransactionCategory
|
||||||
|
import com.wbrawner.budget.data.TransactionType
|
||||||
|
import com.wbrawner.budget.data.model.TransactionWithCategory
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface TransactionDao {
|
interface TransactionDao {
|
||||||
|
@ -13,13 +17,13 @@ interface TransactionDao {
|
||||||
fun save(transaction: Transaction)
|
fun save(transaction: Transaction)
|
||||||
|
|
||||||
@Query("SELECT * FROM `Transaction` WHERE id = :id")
|
@Query("SELECT * FROM `Transaction` WHERE id = :id")
|
||||||
fun load(id: Int): LiveData<Transaction>
|
fun load(id: Int): LiveData<TransactionWithCategory>
|
||||||
|
|
||||||
@Query("SELECT * FROM `Transaction` LIMIT :count")
|
@Query("SELECT * FROM `Transaction` LIMIT :count")
|
||||||
fun loadMultiple(count: Int): LiveData<List<Transaction>>
|
fun loadMultiple(count: Int): LiveData<List<TransactionWithCategory>>
|
||||||
|
|
||||||
@Query("SELECT * FROM `Transaction` WHERE type = :type LIMIT :count")
|
@Query("SELECT * FROM `Transaction` WHERE type = :type LIMIT :count")
|
||||||
fun loadMultipleByType(count: Int, type: TransactionType): LiveData<List<Transaction>>
|
fun loadMultipleByType(count: Int, type: TransactionType): LiveData<List<TransactionWithCategory>>
|
||||||
|
|
||||||
@Query("SELECT (SELECT TOTAL(amount) from `Transaction` WHERE type = 'INCOME') - (SELECT TOTAL(amount) from `Transaction` WHERE type = 'EXPENSE')")
|
@Query("SELECT (SELECT TOTAL(amount) from `Transaction` WHERE type = 'INCOME') - (SELECT TOTAL(amount) from `Transaction` WHERE type = 'EXPENSE')")
|
||||||
fun getBalance(): LiveData<Double>
|
fun getBalance(): LiveData<Double>
|
||||||
|
@ -29,4 +33,7 @@ interface TransactionDao {
|
||||||
|
|
||||||
@Query("DELETE FROM `Transaction` WHERE id = :id")
|
@Query("DELETE FROM `Transaction` WHERE id = :id")
|
||||||
fun deleteById(id: Int)
|
fun deleteById(id: Int)
|
||||||
|
|
||||||
|
@Query("SELECT id,name from `Category`")
|
||||||
|
fun loadCategories(): LiveData<List<TransactionCategory>>
|
||||||
}
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.wbrawner.budget.data.migrations
|
||||||
|
|
||||||
|
import android.arch.persistence.db.SupportSQLiteDatabase
|
||||||
|
import android.arch.persistence.room.migration.Migration
|
||||||
|
|
||||||
|
class MIGRATION_1_2: Migration(1, 2) {
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("CREATE TABLE `Category` (`id` INTEGER, `name` TEXT, `amount` REAL, " +
|
||||||
|
"`repeat` TEXT, `color` INTEGER PRIMARY KEY (`id`))")
|
||||||
|
database.execSQL("ALTER TABLE `Transaction` ADD COLUMN categoryId")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
15
app/src/main/java/com/wbrawner/budget/data/model/Category.kt
Normal file
15
app/src/main/java/com/wbrawner/budget/data/model/Category.kt
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package com.wbrawner.budget.data.model
|
||||||
|
|
||||||
|
import android.arch.persistence.room.Entity
|
||||||
|
import android.arch.persistence.room.PrimaryKey
|
||||||
|
import android.support.annotation.ColorInt
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
class Category(
|
||||||
|
@PrimaryKey
|
||||||
|
val id: Int?,
|
||||||
|
val name: String,
|
||||||
|
val amount: Double,
|
||||||
|
val repeat: String?,
|
||||||
|
@ColorInt val color: Int
|
||||||
|
)
|
|
@ -1,7 +1,8 @@
|
||||||
package com.wbrawner.budget.data
|
package com.wbrawner.budget.data.model
|
||||||
|
|
||||||
import android.arch.persistence.room.Entity
|
import android.arch.persistence.room.Entity
|
||||||
import android.arch.persistence.room.PrimaryKey
|
import android.arch.persistence.room.PrimaryKey
|
||||||
|
import com.wbrawner.budget.data.TransactionType
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
|
@ -12,5 +13,6 @@ class Transaction(
|
||||||
val date: Date,
|
val date: Date,
|
||||||
val description: String,
|
val description: String,
|
||||||
val amount: Double,
|
val amount: Double,
|
||||||
|
val categoryId: Int?,
|
||||||
val type: TransactionType
|
val type: TransactionType
|
||||||
)
|
)
|
|
@ -0,0 +1,10 @@
|
||||||
|
package com.wbrawner.budget.data.model
|
||||||
|
|
||||||
|
class TransactionCategory(
|
||||||
|
val id: Int,
|
||||||
|
val name: String
|
||||||
|
) {
|
||||||
|
override fun toString(): String {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.wbrawner.budget.data.model
|
||||||
|
|
||||||
|
import android.arch.persistence.room.Embedded
|
||||||
|
import android.arch.persistence.room.Relation
|
||||||
|
import com.wbrawner.budget.data.model.Category
|
||||||
|
import com.wbrawner.budget.data.model.Transaction
|
||||||
|
|
||||||
|
class TransactionWithCategory {
|
||||||
|
@Embedded
|
||||||
|
lateinit var transaction: Transaction
|
||||||
|
|
||||||
|
@Relation(parentColumn = "id", entityColumn = "id")
|
||||||
|
lateinit var categorySet: Set<Category>
|
||||||
|
}
|
|
@ -20,12 +20,7 @@ class OverviewFragment : Fragment() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
viewModel = ViewModelProviders.of(activity!!).get(TransactionViewModel::class.java)
|
viewModel = ViewModelProviders.of(activity!!).get(TransactionViewModel::class.java)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
@ -64,4 +59,4 @@ class OverviewFragment : Fragment() {
|
||||||
const val TAG_FRAGMENT = "overview"
|
const val TAG_FRAGMENT = "overview"
|
||||||
const val TITLE_FRAGMENT = R.string.app_name
|
const val TITLE_FRAGMENT = R.string.app_name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.wbrawner.budget.addedittransaction
|
package com.wbrawner.budget.transactions
|
||||||
|
|
||||||
import android.arch.lifecycle.Observer
|
import android.arch.lifecycle.Observer
|
||||||
import android.arch.lifecycle.ViewModelProviders
|
import android.arch.lifecycle.ViewModelProviders
|
||||||
|
@ -6,10 +6,12 @@ import android.os.Bundle
|
||||||
import android.support.v7.app.AppCompatActivity
|
import android.support.v7.app.AppCompatActivity
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
import com.wbrawner.budget.R
|
import com.wbrawner.budget.R
|
||||||
import com.wbrawner.budget.data.Transaction
|
import com.wbrawner.budget.data.model.Transaction
|
||||||
|
import com.wbrawner.budget.data.model.TransactionCategory
|
||||||
import com.wbrawner.budget.data.TransactionType
|
import com.wbrawner.budget.data.TransactionType
|
||||||
import com.wbrawner.budget.transactions.TransactionViewModel
|
import com.wbrawner.budget.data.model.TransactionWithCategory
|
||||||
import kotlinx.android.synthetic.main.activity_add_edit_transaction.*
|
import kotlinx.android.synthetic.main.activity_add_edit_transaction.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
@ -26,6 +28,22 @@ class AddEditTransactionActivity : AppCompatActivity() {
|
||||||
setSupportActionBar(action_bar)
|
setSupportActionBar(action_bar)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
viewModel = ViewModelProviders.of(this).get(TransactionViewModel::class.java)
|
viewModel = ViewModelProviders.of(this).get(TransactionViewModel::class.java)
|
||||||
|
viewModel.getCategories()
|
||||||
|
.observe(this, Observer<List<TransactionCategory>> { categories ->
|
||||||
|
val adapter = ArrayAdapter<TransactionCategory>(
|
||||||
|
this@AddEditTransactionActivity,
|
||||||
|
android.R.layout.simple_list_item_1
|
||||||
|
)
|
||||||
|
|
||||||
|
adapter.add(TransactionCategory(0, getString(R.string.uncategorized)))
|
||||||
|
if (categories == null || categories.isEmpty()) {
|
||||||
|
return@Observer
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter.addAll(categories)
|
||||||
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||||
|
edit_transaction_category.adapter = adapter
|
||||||
|
})
|
||||||
if (intent?.hasExtra(EXTRA_TYPE) == true) {
|
if (intent?.hasExtra(EXTRA_TYPE) == true) {
|
||||||
type = TransactionType.valueOf(intent?.extras?.getString(EXTRA_TYPE, "EXPENSE")
|
type = TransactionType.valueOf(intent?.extras?.getString(EXTRA_TYPE, "EXPENSE")
|
||||||
?: "EXPENSE")
|
?: "EXPENSE")
|
||||||
|
@ -37,11 +55,12 @@ class AddEditTransactionActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.getTransaction(intent!!.extras!!.getInt(EXTRA_TRANSACTION_ID))
|
viewModel.getTransaction(intent!!.extras!!.getInt(EXTRA_TRANSACTION_ID))
|
||||||
.observe(this, Observer<Transaction> { transaction ->
|
.observe(this, Observer<TransactionWithCategory> { transactionWithCategory ->
|
||||||
if (transaction == null) {
|
if (transactionWithCategory == null) {
|
||||||
menu?.findItem(R.id.action_delete)?.isVisible = false
|
menu?.findItem(R.id.action_delete)?.isVisible = false
|
||||||
return@Observer
|
return@Observer
|
||||||
}
|
}
|
||||||
|
val transaction = transactionWithCategory.transaction
|
||||||
id = transaction.id
|
id = transaction.id
|
||||||
type = transaction.type
|
type = transaction.type
|
||||||
setTitle(type.editTitle)
|
setTitle(type.editTitle)
|
||||||
|
@ -55,6 +74,15 @@ class AddEditTransactionActivity : AppCompatActivity() {
|
||||||
val month = field.get(Calendar.MONTH)
|
val month = field.get(Calendar.MONTH)
|
||||||
val day = field.get(Calendar.DAY_OF_MONTH)
|
val day = field.get(Calendar.DAY_OF_MONTH)
|
||||||
edit_transaction_date.updateDate(year, month, day)
|
edit_transaction_date.updateDate(year, month, day)
|
||||||
|
if (transactionWithCategory.categorySet.isNotEmpty()) {
|
||||||
|
val category = transactionWithCategory.categorySet.first()
|
||||||
|
for (i in 0 until edit_transaction_category.adapter.count) {
|
||||||
|
if (category.id == (edit_transaction_category.adapter.getItem(i) as TransactionCategory).id) {
|
||||||
|
edit_transaction_category.setSelection(i)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +109,8 @@ class AddEditTransactionActivity : AppCompatActivity() {
|
||||||
date = cal.time,
|
date = cal.time,
|
||||||
description = edit_transaction_description.text.toString(),
|
description = edit_transaction_description.text.toString(),
|
||||||
amount = edit_transaction_amount.text.toString().toDouble(),
|
amount = edit_transaction_amount.text.toString().toDouble(),
|
||||||
type = type
|
type = type,
|
||||||
|
categoryId = (edit_transaction_category.selectedItem as TransactionCategory).id
|
||||||
))
|
))
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
|
@ -8,13 +8,15 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import com.wbrawner.budget.R
|
import com.wbrawner.budget.R
|
||||||
import com.wbrawner.budget.addedittransaction.AddEditTransactionActivity
|
import com.wbrawner.budget.transactions.AddEditTransactionActivity.Companion.EXTRA_TRANSACTION_ID
|
||||||
import com.wbrawner.budget.addedittransaction.AddEditTransactionActivity.Companion.EXTRA_TRANSACTION_ID
|
import com.wbrawner.budget.data.model.Transaction
|
||||||
import com.wbrawner.budget.data.Transaction
|
import com.wbrawner.budget.data.model.TransactionWithCategory
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
|
||||||
class TransactionAdapter(private val data: List<Transaction>)
|
class TransactionAdapter() : RecyclerView.Adapter<TransactionAdapter.ViewHolder>() {
|
||||||
: RecyclerView.Adapter<TransactionAdapter.ViewHolder>() {
|
private lateinit var data: List<Any>
|
||||||
|
private lateinit var listType: Class<Any>
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
val view = LayoutInflater.from(parent.context)
|
val view = LayoutInflater.from(parent.context)
|
||||||
.inflate(R.layout.list_item_transaction, parent, false)
|
.inflate(R.layout.list_item_transaction, parent, false)
|
||||||
|
@ -24,7 +26,9 @@ class TransactionAdapter(private val data: List<Transaction>)
|
||||||
override fun getItemCount(): Int = data.size
|
override fun getItemCount(): Int = data.size
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
val transaction = data[position]
|
val transaction: Transaction =
|
||||||
|
if (listType == TransactionWithCategory::class.java) (data[position] as TransactionWithCategory).transaction
|
||||||
|
else data[position] as Transaction
|
||||||
holder.title.text = transaction.title
|
holder.title.text = transaction.title
|
||||||
holder.date.text = SimpleDateFormat.getDateInstance(SimpleDateFormat.SHORT).format(transaction.date)
|
holder.date.text = SimpleDateFormat.getDateInstance(SimpleDateFormat.SHORT).format(transaction.date)
|
||||||
holder.amount.text = String.format("${'$'}%.02f", transaction.amount)
|
holder.amount.text = String.format("${'$'}%.02f", transaction.amount)
|
||||||
|
@ -44,7 +48,16 @@ class TransactionAdapter(private val data: List<Transaction>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
|
constructor(transactions: List<Any>) : this() {
|
||||||
|
if (transactions.isEmpty()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
listType = transactions.first().javaClass
|
||||||
|
data = transactions
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
val title = itemView.findViewById<TextView>(R.id.transaction_title)
|
val title = itemView.findViewById<TextView>(R.id.transaction_title)
|
||||||
val date = itemView.findViewById<TextView>(R.id.transaction_date)
|
val date = itemView.findViewById<TextView>(R.id.transaction_date)
|
||||||
val amount = itemView.findViewById<TextView>(R.id.transaction_amount)
|
val amount = itemView.findViewById<TextView>(R.id.transaction_amount)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import android.support.text.emoji.EmojiCompat
|
||||||
import android.support.text.emoji.bundled.BundledEmojiCompatConfig
|
import android.support.text.emoji.bundled.BundledEmojiCompatConfig
|
||||||
import android.support.v7.app.AppCompatActivity
|
import android.support.v7.app.AppCompatActivity
|
||||||
import com.wbrawner.budget.R
|
import com.wbrawner.budget.R
|
||||||
|
import com.wbrawner.budget.categories.CategoryListFragment
|
||||||
import com.wbrawner.budget.data.TransactionType
|
import com.wbrawner.budget.data.TransactionType
|
||||||
import com.wbrawner.budget.overview.OverviewFragment
|
import com.wbrawner.budget.overview.OverviewFragment
|
||||||
import kotlinx.android.synthetic.main.activity_transaction_list.*
|
import kotlinx.android.synthetic.main.activity_transaction_list.*
|
||||||
|
@ -21,6 +22,7 @@ class TransactionListActivity : AppCompatActivity() {
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.action_expenses -> updateFragment(TransactionType.EXPENSE)
|
R.id.action_expenses -> updateFragment(TransactionType.EXPENSE)
|
||||||
R.id.action_income -> updateFragment(TransactionType.INCOME)
|
R.id.action_income -> updateFragment(TransactionType.INCOME)
|
||||||
|
R.id.action_categories -> updateFragment(CategoryListFragment.TAG_FRAGMENT, CategoryListFragment.TITLE_FRAGMENT)
|
||||||
else ->
|
else ->
|
||||||
updateFragment(OverviewFragment.TAG_FRAGMENT, OverviewFragment.TITLE_FRAGMENT)
|
updateFragment(OverviewFragment.TAG_FRAGMENT, OverviewFragment.TITLE_FRAGMENT)
|
||||||
}
|
}
|
||||||
|
@ -38,13 +40,17 @@ class TransactionListActivity : AppCompatActivity() {
|
||||||
var fragment = supportFragmentManager.findFragmentByTag(tag)
|
var fragment = supportFragmentManager.findFragmentByTag(tag)
|
||||||
val ft = supportFragmentManager.beginTransaction()
|
val ft = supportFragmentManager.beginTransaction()
|
||||||
if (fragment == null) {
|
if (fragment == null) {
|
||||||
fragment = if (tag == "overview") OverviewFragment()
|
fragment = when (tag) {
|
||||||
else
|
OverviewFragment.TAG_FRAGMENT -> OverviewFragment()
|
||||||
TransactionListFragment().apply {
|
CategoryListFragment.TAG_FRAGMENT -> CategoryListFragment()
|
||||||
arguments = Bundle().apply {
|
else -> {
|
||||||
putSerializable(TransactionListFragment.ARG_TYPE, TransactionType.valueOf(tag))
|
TransactionListFragment().apply {
|
||||||
|
arguments = Bundle().apply {
|
||||||
|
putSerializable(TransactionListFragment.ARG_TYPE, TransactionType.valueOf(tag))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
ft.add(R.id.content_container, fragment, tag)
|
ft.add(R.id.content_container, fragment, tag)
|
||||||
}
|
}
|
||||||
for (fmFragment in supportFragmentManager.fragments) {
|
for (fmFragment in supportFragmentManager.fragments) {
|
||||||
|
|
|
@ -13,10 +13,9 @@ import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import com.wbrawner.budget.R
|
import com.wbrawner.budget.R
|
||||||
import com.wbrawner.budget.addedittransaction.AddEditTransactionActivity
|
import com.wbrawner.budget.transactions.AddEditTransactionActivity.Companion.EXTRA_TYPE
|
||||||
import com.wbrawner.budget.addedittransaction.AddEditTransactionActivity.Companion.EXTRA_TYPE
|
|
||||||
import com.wbrawner.budget.data.Transaction
|
|
||||||
import com.wbrawner.budget.data.TransactionType
|
import com.wbrawner.budget.data.TransactionType
|
||||||
|
import com.wbrawner.budget.data.model.TransactionWithCategory
|
||||||
|
|
||||||
class TransactionListFragment : Fragment() {
|
class TransactionListFragment : Fragment() {
|
||||||
lateinit var viewModel: TransactionViewModel
|
lateinit var viewModel: TransactionViewModel
|
||||||
|
@ -43,7 +42,7 @@ class TransactionListFragment : Fragment() {
|
||||||
val fab = view.findViewById<FloatingActionButton>(R.id.fab_add_transaction)
|
val fab = view.findViewById<FloatingActionButton>(R.id.fab_add_transaction)
|
||||||
recyclerView.layoutManager = LinearLayoutManager(activity)
|
recyclerView.layoutManager = LinearLayoutManager(activity)
|
||||||
viewModel.getTransactionsByType(20, type)
|
viewModel.getTransactionsByType(20, type)
|
||||||
.observe(this, Observer<List<Transaction>> { data ->
|
.observe(this, Observer<List<TransactionWithCategory>> { data ->
|
||||||
val noDataView = view.findViewById<EmojiTextView>(R.id.transaction_list_no_data)
|
val noDataView = view.findViewById<EmojiTextView>(R.id.transaction_list_no_data)
|
||||||
if (data == null || data.isEmpty()) {
|
if (data == null || data.isEmpty()) {
|
||||||
recyclerView.adapter = null
|
recyclerView.adapter = null
|
||||||
|
|
|
@ -4,22 +4,25 @@ import android.app.Application
|
||||||
import android.arch.lifecycle.AndroidViewModel
|
import android.arch.lifecycle.AndroidViewModel
|
||||||
import android.arch.lifecycle.LiveData
|
import android.arch.lifecycle.LiveData
|
||||||
import com.wbrawner.budget.AllowanceApplication
|
import com.wbrawner.budget.AllowanceApplication
|
||||||
import com.wbrawner.budget.data.Transaction
|
import com.wbrawner.budget.data.*
|
||||||
import com.wbrawner.budget.data.TransactionRepository
|
import com.wbrawner.budget.data.model.Transaction
|
||||||
import com.wbrawner.budget.data.TransactionType
|
import com.wbrawner.budget.data.model.TransactionCategory
|
||||||
|
import com.wbrawner.budget.data.model.TransactionWithCategory
|
||||||
|
|
||||||
class TransactionViewModel(application: Application): AndroidViewModel(application) {
|
class TransactionViewModel(application: Application): AndroidViewModel(application) {
|
||||||
private val transactionRepo = TransactionRepository((application as AllowanceApplication).dao)
|
private val transactionRepo = TransactionRepository((application as AllowanceApplication).transactionDao)
|
||||||
|
|
||||||
fun getTransaction(id: Int): LiveData<Transaction> = transactionRepo.getTransaction(id)
|
fun getTransaction(id: Int): LiveData<TransactionWithCategory> = transactionRepo.getTransaction(id)
|
||||||
|
|
||||||
fun getTransactions(count: Int): LiveData<List<Transaction>> = transactionRepo.getTransactions(count)
|
fun getTransactions(count: Int): LiveData<List<TransactionWithCategory>> = transactionRepo.getTransactions(count)
|
||||||
|
|
||||||
fun getTransactionsByType(count: Int, type: TransactionType): LiveData<List<Transaction>>
|
fun getTransactionsByType(count: Int, type: TransactionType): LiveData<List<TransactionWithCategory>>
|
||||||
= transactionRepo.getTransactionsByType(count, type)
|
= transactionRepo.getTransactionsByType(count, type)
|
||||||
|
|
||||||
fun getCurrentBalance(): LiveData<Double> = transactionRepo.getCurrentBalance()
|
fun getCurrentBalance(): LiveData<Double> = transactionRepo.getCurrentBalance()
|
||||||
|
|
||||||
|
fun getCategories(): LiveData<List<TransactionCategory>> = transactionRepo.getCategories()
|
||||||
|
|
||||||
fun saveTransaction(transaction: Transaction) = transactionRepo.save(transaction)
|
fun saveTransaction(transaction: Transaction) = transactionRepo.save(transaction)
|
||||||
|
|
||||||
fun deleteTransaction(transaction: Transaction) = transactionRepo.delete(transaction)
|
fun deleteTransaction(transaction: Transaction) = transactionRepo.delete(transaction)
|
||||||
|
|
15
app/src/main/res/drawable/ic_baseline_category_24px.xml
Normal file
15
app/src/main/res/drawable/ic_baseline_category_24px.xml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M12,2l-5.5,9h11z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M17.5,17.5m-4.5,0a4.5,4.5 0,1 1,9 0a4.5,4.5 0,1 1,-9 0"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M3,13.5h8v8H3z"/>
|
||||||
|
</vector>
|
81
app/src/main/res/layout/activity_add_edit_category.xml
Normal file
81
app/src/main/res/layout/activity_add_edit_category.xml
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<android.support.v7.widget.Toolbar
|
||||||
|
android:id="@+id/action_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:elevation="4dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:id="@+id/scrollView2"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/action_bar">
|
||||||
|
|
||||||
|
<android.support.constraint.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:id="@+id/container_edit_category_name"
|
||||||
|
style="@style/AppTheme.EditText.Container"
|
||||||
|
android:hint="@string/prompt_category_name"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/container_edit_category_amount"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputEditText
|
||||||
|
android:id="@+id/edit_category_name"
|
||||||
|
style="@style/AppTheme.EditText"
|
||||||
|
android:inputType="textCapWords" />
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:id="@+id/container_edit_category_amount"
|
||||||
|
style="@style/AppTheme.EditText.Container"
|
||||||
|
android:hint="@string/prompt_category_amount"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/container_edit_category_name">
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputEditText
|
||||||
|
android:id="@+id/edit_category_amount"
|
||||||
|
style="@style/AppTheme.EditText"
|
||||||
|
android:inputType="numberDecimal" />
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
<!--<TextView-->
|
||||||
|
<!--android:id="@+id/container_edit_transaction_date"-->
|
||||||
|
<!--style="@style/AppTheme.EditText.Hint"-->
|
||||||
|
<!--android:layout_width="match_parent"-->
|
||||||
|
<!--android:layout_height="wrap_content"-->
|
||||||
|
<!--android:layout_marginStart="4dp"-->
|
||||||
|
<!--android:text="@string/prompt_transaction_date"-->
|
||||||
|
<!--app:layout_constraintBottom_toTopOf="@+id/edit_transaction_date"-->
|
||||||
|
<!--app:layout_constraintEnd_toEndOf="parent"-->
|
||||||
|
<!--app:layout_constraintStart_toStartOf="parent"-->
|
||||||
|
<!--app:layout_constraintTop_toBottomOf="@+id/container_edit_transaction_amount" />-->
|
||||||
|
|
||||||
|
<!--<DatePicker-->
|
||||||
|
<!--android:id="@+id/edit_transaction_date"-->
|
||||||
|
<!--style="@style/AppTheme.DatePicker"-->
|
||||||
|
<!--app:layout_constraintBottom_toBottomOf="parent"-->
|
||||||
|
<!--app:layout_constraintEnd_toEndOf="parent"-->
|
||||||
|
<!--app:layout_constraintStart_toStartOf="parent"-->
|
||||||
|
<!--app:layout_constraintTop_toBottomOf="@+id/container_edit_transaction_date" />-->
|
||||||
|
</android.support.constraint.ConstraintLayout>
|
||||||
|
</ScrollView>
|
||||||
|
</android.support.constraint.ConstraintLayout>
|
|
@ -88,10 +88,31 @@
|
||||||
<DatePicker
|
<DatePicker
|
||||||
android:id="@+id/edit_transaction_date"
|
android:id="@+id/edit_transaction_date"
|
||||||
style="@style/AppTheme.DatePicker"
|
style="@style/AppTheme.DatePicker"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toTopOf="@+id/container_edit_transaction_category"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/container_edit_transaction_date" />
|
app:layout_constraintTop_toBottomOf="@+id/container_edit_transaction_date" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/container_edit_transaction_category"
|
||||||
|
style="@style/AppTheme.EditText.Hint"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:text="@string/prompt_transaction_category"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/edit_transaction_category"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/edit_transaction_date" />
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/edit_transaction_category"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/container_edit_transaction_category" />
|
||||||
</android.support.constraint.ConstraintLayout>
|
</android.support.constraint.ConstraintLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</android.support.constraint.ConstraintLayout>
|
</android.support.constraint.ConstraintLayout>
|
||||||
|
|
50
app/src/main/res/layout/list_item_category.xml
Normal file
50
app/src/main/res/layout/list_item_category.xml
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/category_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/barrier"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/category_amount"
|
||||||
|
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/category_amount"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="18sp"
|
||||||
|
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/barrier"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/category_title"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
|
<android.support.constraint.Barrier
|
||||||
|
android:id="@+id/barrier"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:barrierDirection="bottom"
|
||||||
|
app:constraint_referenced_ids="category_title,category_amount" />
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/category_progress"
|
||||||
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:indeterminate="true"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/barrier"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/barrier" />
|
||||||
|
</android.support.constraint.ConstraintLayout>
|
|
@ -12,4 +12,8 @@
|
||||||
android:id="@+id/action_income"
|
android:id="@+id/action_income"
|
||||||
android:icon="@drawable/ic_attach_money_black_24dp"
|
android:icon="@drawable/ic_attach_money_black_24dp"
|
||||||
android:title="@string/title_income" />
|
android:title="@string/title_income" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_categories"
|
||||||
|
android:icon="@drawable/ic_baseline_category_24px"
|
||||||
|
android:title="@string/title_categories" />
|
||||||
</menu>
|
</menu>
|
|
@ -19,4 +19,13 @@
|
||||||
<string name="title_edit_expense">Edit Expense</string>
|
<string name="title_edit_expense">Edit Expense</string>
|
||||||
<string name="income_no_data">🌳\nGet to work! Money doesn\'t grow on trees after all</string>
|
<string name="income_no_data">🌳\nGet to work! Money doesn\'t grow on trees after all</string>
|
||||||
<string name="expenses_no_data">🤔\nAre you sure you haven\'t spent any money?</string>
|
<string name="expenses_no_data">🤔\nAre you sure you haven\'t spent any money?</string>
|
||||||
|
<string name="title_categories">Categories</string>
|
||||||
|
<string name="title_edit_category">Edit Category</string>
|
||||||
|
<string name="prompt_category_amount">Amount</string>
|
||||||
|
<string name="prompt_category_name">Name</string>
|
||||||
|
<string name="title_add_category">Add Category</string>
|
||||||
|
<string name="uncategorized">Uncategorized</string>
|
||||||
|
<string name="prompt_transaction_category">Category</string>
|
||||||
|
<string name="required_field_name">Name is a required field</string>
|
||||||
|
<string name="required_field_amount">Amount is a required field</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<intent
|
<intent
|
||||||
android:action="android.intent.action.VIEW"
|
android:action="android.intent.action.VIEW"
|
||||||
android:targetPackage="com.wbrawner.budget"
|
android:targetPackage="com.wbrawner.budget"
|
||||||
android:targetClass="com.wbrawner.budget.addedittransaction.AddEditTransactionActivity">
|
android:targetClass="com.wbrawner.budget.transactions.AddEditTransactionActivity">
|
||||||
<extra
|
<extra
|
||||||
android:name="EXTRA_TRANSACTION_TYPE"
|
android:name="EXTRA_TRANSACTION_TYPE"
|
||||||
android:value="EXPENSE" />
|
android:value="EXPENSE" />
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
<intent
|
<intent
|
||||||
android:action="android.intent.action.VIEW"
|
android:action="android.intent.action.VIEW"
|
||||||
android:targetPackage="com.wbrawner.budget"
|
android:targetPackage="com.wbrawner.budget"
|
||||||
android:targetClass="com.wbrawner.budget.addedittransaction.AddEditTransactionActivity">
|
android:targetClass="com.wbrawner.budget.transactions.AddEditTransactionActivity">
|
||||||
<extra
|
<extra
|
||||||
android:name="EXTRA_TRANSACTION_TYPE"
|
android:name="EXTRA_TRANSACTION_TYPE"
|
||||||
android:value="INCOME" />
|
android:value="INCOME" />
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.2.41'
|
ext.kotlin_version = '1.3.0'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.1.4'
|
classpath 'com.android.tools.build:gradle:3.2.1'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
|
||||||
|
|
Loading…
Reference in a new issue