Update database structure to match server
This commit is contained in:
parent
85a91d0af0
commit
f3e456ac28
35 changed files with 474 additions and 209 deletions
|
@ -1,9 +1,6 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
|
||||
|
@ -50,16 +47,17 @@ dependencies {
|
|||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'com.android.support:appcompat-v7:27.1.1'
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
||||
implementation 'com.android.support:design:27.1.1'
|
||||
implementation "com.android.support:support-emoji-bundled:27.1.1"
|
||||
implementation 'com.github.BlacKCaT27:CurrencyEditText:2.0.2'
|
||||
def room_version = "1.1.1"
|
||||
def lifecycle_version = "1.1.1"
|
||||
// ViewModel and LiveData
|
||||
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
|
||||
implementation "android.arch.persistence.room:runtime:$room_version"
|
||||
kapt "android.arch.persistence.room:compiler:$room_version"
|
||||
testImplementation "android.arch.persistence.room:testing:$room_version"
|
||||
androidTestImplementation "android.arch.persistence.room:testing:$room_version"
|
||||
testImplementation 'junit:junit:4.12'
|
||||
|
||||
// ACRA Crash reporting
|
||||
|
|
63
app/schemas/com.wbrawner.budget.data.BudgetDatabase/1.json
Normal file
63
app/schemas/com.wbrawner.budget.data.BudgetDatabase/1.json
Normal file
|
@ -0,0 +1,63 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "da32c301dc558cb73099ff325bb88fb7",
|
||||
"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, `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": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"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, \"da32c301dc558cb73099ff325bb88fb7\")"
|
||||
]
|
||||
}
|
||||
}
|
125
app/schemas/com.wbrawner.budget.data.BudgetDatabase/3.json
Normal file
125
app/schemas/com.wbrawner.budget.data.BudgetDatabase/3.json
Normal file
|
@ -0,0 +1,125 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 3,
|
||||
"identityHash": "68f2dbe4273bb745930a348dc280ae96",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "Transaction",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER, `remoteId` TEXT, `name` TEXT NOT NULL, `date` TEXT NOT NULL, `description` TEXT NOT NULL, `amount` INTEGER NOT NULL, `categoryId` INTEGER, `isExpense` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "remoteId",
|
||||
"columnName": "remoteId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"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": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "categoryId",
|
||||
"columnName": "categoryId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isExpense",
|
||||
"columnName": "isExpense",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "Category",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER, `remoteId` TEXT, `name` TEXT NOT NULL, `amount` INTEGER NOT NULL, `repeat` TEXT, `color` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "remoteId",
|
||||
"columnName": "remoteId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "amount",
|
||||
"columnName": "amount",
|
||||
"affinity": "INTEGER",
|
||||
"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, \"68f2dbe4273bb745930a348dc280ae96\")"
|
||||
]
|
||||
}
|
||||
}
|
109
app/src/androidTest/java/com/wbrawner/budget/MigrationTests.java
Normal file
109
app/src/androidTest/java/com/wbrawner/budget/MigrationTests.java
Normal file
|
@ -0,0 +1,109 @@
|
|||
package com.wbrawner.budget;
|
||||
|
||||
import android.arch.persistence.db.SupportSQLiteDatabase;
|
||||
import android.arch.persistence.db.framework.FrameworkSQLiteOpenHelperFactory;
|
||||
import android.arch.persistence.room.testing.MigrationTestHelper;
|
||||
import android.database.Cursor;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.wbrawner.budget.data.BudgetDatabase;
|
||||
import com.wbrawner.budget.data.migrations.MIGRATION_1_2;
|
||||
import com.wbrawner.budget.data.migrations.MIGRATION_2_3;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static junit.framework.Assert.assertNull;
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class MigrationTests {
|
||||
private static final String TEST_DB = "migration-test";
|
||||
|
||||
@Rule
|
||||
public MigrationTestHelper migrationHelper;
|
||||
|
||||
public MigrationTests() {
|
||||
this.migrationHelper = new MigrationTestHelper(
|
||||
InstrumentationRegistry.getInstrumentation(),
|
||||
BudgetDatabase.class.getCanonicalName(),
|
||||
new FrameworkSQLiteOpenHelperFactory()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate1to2() throws IOException {
|
||||
SupportSQLiteDatabase db = migrationHelper.createDatabase(TEST_DB, 1);
|
||||
db.execSQL("INSERT INTO `Transaction` (id,title,date,description,amount,type) " +
|
||||
"VALUES (1,'An expense','2018-10-31','Spent some money',12.34,'EXPENSE')");
|
||||
db.close();
|
||||
db = migrationHelper.runMigrationsAndValidate(
|
||||
TEST_DB,
|
||||
2,
|
||||
true,
|
||||
new MIGRATION_1_2()
|
||||
);
|
||||
Cursor cursor = db.query("SELECT * FROM 'Transaction'");
|
||||
assertEquals(1, cursor.getCount());
|
||||
cursor.moveToFirst();
|
||||
assertEquals(1, cursor.getInt(cursor.getColumnIndex("id")));
|
||||
assertEquals("An expense", cursor.getString(cursor.getColumnIndex("title")));
|
||||
assertEquals("2018-10-31", cursor.getString(cursor.getColumnIndex("date")));
|
||||
assertEquals("Spent some money", cursor.getString(cursor.getColumnIndex("description")));
|
||||
assertEquals(12.34, cursor.getDouble(cursor.getColumnIndex("amount")));
|
||||
assertEquals(0, cursor.getInt(cursor.getColumnIndex("categoryId")));
|
||||
assertEquals("EXPENSE", cursor.getString(cursor.getColumnIndex("type")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate2to3() throws IOException {
|
||||
SupportSQLiteDatabase db = migrationHelper.createDatabase(TEST_DB, 2);
|
||||
db.execSQL("INSERT INTO `Transaction` (id,title,date,description,amount,categoryId,type) " +
|
||||
"VALUES (1,'An expense','2018-10-31','Spent some money',12.34,1,'EXPENSE')");
|
||||
db.execSQL("INSERT INTO `Transaction` (id,title,date,description,amount,categoryId,type) " +
|
||||
"VALUES (2,'Some income','2018-01-02','Made some money',42.65,0,'INCOME')");
|
||||
db.execSQL("INSERT INTO `Category` (id,name,amount,repeat,color) " +
|
||||
"VALUES (1,'Groceries',1234.56,'monthly',987)");
|
||||
db.close();
|
||||
db = migrationHelper.runMigrationsAndValidate(
|
||||
TEST_DB,
|
||||
3,
|
||||
true,
|
||||
new MIGRATION_2_3()
|
||||
);
|
||||
Cursor cursor = db.query("SELECT * FROM 'Transaction'");
|
||||
assertEquals(2, cursor.getCount());
|
||||
assertTrue(cursor.moveToFirst());
|
||||
assertEquals(1, cursor.getInt(cursor.getColumnIndex("id")));
|
||||
assertNull(cursor.getString(cursor.getColumnIndex("remoteId")));
|
||||
assertEquals("An expense", cursor.getString(cursor.getColumnIndex("name")));
|
||||
assertEquals("2018-10-31", cursor.getString(cursor.getColumnIndex("date")));
|
||||
assertEquals("Spent some money", cursor.getString(cursor.getColumnIndex("description")));
|
||||
assertEquals(1234, cursor.getInt(cursor.getColumnIndex("amount")));
|
||||
assertEquals(1, cursor.getInt(cursor.getColumnIndex("categoryId")));
|
||||
assertEquals(1, cursor.getInt(cursor.getColumnIndex("isExpense")));
|
||||
assertTrue(cursor.moveToNext());
|
||||
assertEquals(2, cursor.getInt(cursor.getColumnIndex("id")));
|
||||
assertNull(cursor.getString(cursor.getColumnIndex("remoteId")));
|
||||
assertEquals("Some income", cursor.getString(cursor.getColumnIndex("name")));
|
||||
assertEquals("2018-01-02", cursor.getString(cursor.getColumnIndex("date")));
|
||||
assertEquals("Made some money", cursor.getString(cursor.getColumnIndex("description")));
|
||||
assertEquals(4265, cursor.getInt(cursor.getColumnIndex("amount")));
|
||||
assertEquals(0, cursor.getInt(cursor.getColumnIndex("categoryId")));
|
||||
assertEquals(0, cursor.getInt(cursor.getColumnIndex("isExpense")));
|
||||
cursor = db.query("SELECT * FROM 'Category'");
|
||||
assertEquals(1, cursor.getCount());
|
||||
assertTrue(cursor.moveToFirst());
|
||||
assertEquals(1, cursor.getInt(cursor.getColumnIndex("id")));
|
||||
assertNull(cursor.getString(cursor.getColumnIndex("remoteId")));
|
||||
assertEquals("Groceries", cursor.getString(cursor.getColumnIndex("name")));
|
||||
assertEquals(123456, cursor.getInt(cursor.getColumnIndex("amount")));
|
||||
assertEquals("monthly", cursor.getString(cursor.getColumnIndex("repeat")));
|
||||
assertEquals(987, cursor.getInt(cursor.getColumnIndex("color")));
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@
|
|||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity android:name="com.wbrawner.budget.transactions.TransactionListActivity">
|
||||
<activity android:name="com.wbrawner.budget.ui.MainActivity">
|
||||
<intent-filter android:label="@string/app_name_short">
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
|
@ -20,8 +20,8 @@
|
|||
android:name="android.app.shortcuts"
|
||||
android:resource="@xml/shortcuts" />
|
||||
</activity>
|
||||
<activity android:name="com.wbrawner.budget.transactions.AddEditTransactionActivity" />
|
||||
<activity android:name="com.wbrawner.budget.categories.AddEditCategoryActivity" />
|
||||
<activity android:name="com.wbrawner.budget.ui.transactions.AddEditTransactionActivity" />
|
||||
<activity android:name="com.wbrawner.budget.ui.categories.AddEditCategoryActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -7,6 +7,7 @@ import com.wbrawner.budget.data.dao.TransactionDao
|
|||
import com.wbrawner.budget.data.BudgetDatabase
|
||||
import com.wbrawner.budget.data.dao.CategoryDao
|
||||
import com.wbrawner.budget.data.migrations.MIGRATION_1_2
|
||||
import com.wbrawner.budget.data.migrations.MIGRATION_2_3
|
||||
import org.acra.ACRA
|
||||
import org.acra.annotation.AcraCore
|
||||
import org.acra.annotation.AcraHttpSender
|
||||
|
@ -33,6 +34,7 @@ class AllowanceApplication: Application() {
|
|||
|
||||
database = Room.databaseBuilder(applicationContext, BudgetDatabase::class.java, "transactions")
|
||||
.addMigrations(MIGRATION_1_2())
|
||||
.addMigrations(MIGRATION_2_3())
|
||||
.build()
|
||||
transactionDao = database.transactionDao()
|
||||
categoryDao = database.categoryDao()
|
||||
|
|
|
@ -8,8 +8,8 @@ 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)
|
||||
@Database(entities = [(Transaction::class), (Category::class)], version = 3)
|
||||
@TypeConverters(DateTypeConverter::class)
|
||||
abstract class BudgetDatabase: RoomDatabase() {
|
||||
abstract fun transactionDao(): TransactionDao
|
||||
abstract fun categoryDao(): CategoryDao
|
||||
|
|
|
@ -37,7 +37,7 @@ class CategoryRepository(private val dao: CategoryDao) {
|
|||
}
|
||||
|
||||
|
||||
fun getCurrentBalance(id: Int): LiveData<Double> = dao.getBalanceForCategory(id)
|
||||
fun getCurrentBalance(id: Int): LiveData<Int> = dao.getBalanceForCategory(id)
|
||||
|
||||
|
||||
fun getTransactions(id: Int): LiveData<List<Transaction>> = dao.getTransactions(id)
|
||||
|
|
|
@ -17,8 +17,8 @@ class TransactionRepository(private val dao: TransactionDao) {
|
|||
handler = Handler(thread.looper)
|
||||
}
|
||||
|
||||
fun getTransactionsByType(count: Int, type: TransactionType): LiveData<List<TransactionWithCategory>> =
|
||||
dao.loadMultipleByType(count, type)
|
||||
// fun getTransactionsByType(count: Int, type: TransactionType): LiveData<List<TransactionWithCategory>> =
|
||||
// dao.loadMultipleByType(count, type)
|
||||
|
||||
fun getTransactions(count: Int): LiveData<List<TransactionWithCategory>> = dao.loadMultiple(count)
|
||||
|
||||
|
@ -41,7 +41,7 @@ class TransactionRepository(private val dao: TransactionDao) {
|
|||
}
|
||||
|
||||
|
||||
fun getCurrentBalance(): LiveData<Double> = dao.getBalance()
|
||||
fun getCurrentBalance(): LiveData<Int> = dao.getBalance()
|
||||
|
||||
fun getCategories(): LiveData<List<TransactionCategory>> = dao.loadCategories()
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package com.wbrawner.budget.data
|
||||
|
||||
import android.support.annotation.ColorRes
|
||||
import android.support.annotation.StringRes
|
||||
import com.wbrawner.budget.R
|
||||
import java.io.Serializable
|
||||
|
||||
enum class TransactionType(
|
||||
@StringRes val title: Int,
|
||||
@StringRes val addTitle: Int,
|
||||
@StringRes val editTitle: Int,
|
||||
@StringRes val noDataText: Int,
|
||||
@ColorRes val textColor: Int
|
||||
) : Serializable {
|
||||
INCOME(
|
||||
R.string.title_income,
|
||||
R.string.title_add_income,
|
||||
R.string.title_edit_income,
|
||||
R.string.income_no_data,
|
||||
R.color.colorTextGreen
|
||||
),
|
||||
EXPENSE(
|
||||
R.string.title_expenses,
|
||||
R.string.title_add_expense,
|
||||
R.string.title_edit_expense,
|
||||
R.string.expenses_no_data,
|
||||
R.color.colorTextRed
|
||||
);
|
||||
}
|
|
@ -4,14 +4,6 @@ import android.arch.persistence.room.TypeConverter
|
|||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
class TransactionTypeTypeConverter {
|
||||
@android.arch.persistence.room.TypeConverter
|
||||
fun toTransactionType(value: String): TransactionType? = TransactionType.valueOf(value)
|
||||
|
||||
@android.arch.persistence.room.TypeConverter
|
||||
fun toString(type: TransactionType?): String? = type?.name
|
||||
}
|
||||
|
||||
class DateTypeConverter {
|
||||
@android.arch.persistence.room.TypeConverter
|
||||
fun toDate(value: String): Date? = dateFormat.parse(value)
|
||||
|
|
|
@ -21,9 +21,9 @@ interface CategoryDao {
|
|||
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>
|
||||
"(SELECT TOTAL(amount) from `Transaction` WHERE isExpense = 0 AND categoryId = :categoryId) " +
|
||||
"- (SELECT TOTAL(amount) from `Transaction` WHERE isExpense = 1 AND categoryId = :categoryId)")
|
||||
fun getBalanceForCategory(categoryId: Int): LiveData<Int>
|
||||
|
||||
@Delete
|
||||
fun delete(category: Category)
|
||||
|
|
|
@ -8,7 +8,6 @@ import android.arch.persistence.room.OnConflictStrategy.REPLACE
|
|||
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
|
||||
|
@ -22,11 +21,8 @@ interface TransactionDao {
|
|||
@Query("SELECT * FROM `Transaction` LIMIT :count")
|
||||
fun loadMultiple(count: Int): LiveData<List<TransactionWithCategory>>
|
||||
|
||||
@Query("SELECT * FROM `Transaction` WHERE type = :type LIMIT :count")
|
||||
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')")
|
||||
fun getBalance(): LiveData<Double>
|
||||
@Query("SELECT (SELECT TOTAL(amount) from `Transaction` WHERE isExpense = 0) - (SELECT TOTAL(amount) from `Transaction` WHERE isExpense = 1)")
|
||||
fun getBalance(): LiveData<Int>
|
||||
|
||||
@Delete
|
||||
fun delete(transaction: Transaction)
|
||||
|
|
|
@ -5,9 +5,8 @@ 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")
|
||||
database.execSQL("CREATE TABLE `Category` (`id` INTEGER, `name` TEXT NOT NULL, `amount` REAL NOT NULL, `repeat` TEXT, `color` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY (`id`))")
|
||||
database.execSQL("ALTER TABLE `Transaction` ADD COLUMN categoryId INTEGER")
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.wbrawner.budget.data.migrations
|
||||
|
||||
import android.arch.persistence.db.SupportSQLiteDatabase
|
||||
import android.arch.persistence.room.migration.Migration
|
||||
|
||||
class MIGRATION_2_3: Migration(2, 3) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE `Transaction` RENAME TO `TransactionOld`")
|
||||
database.execSQL("CREATE TABLE `Transaction` (`id` INTEGER,`remoteId` TEXT,`name` TEXT NOT NULL,`date` TEXT NOT NULL, `description` TEXT NOT NULL, `amount` INTEGER NOT NULL,`isExpense` INTEGER NOT NULL DEFAULT 1,`categoryId` INTEGER,PRIMARY KEY (`id`))")
|
||||
database.execSQL("INSERT INTO `Transaction` ( id, name, date, description, amount, categoryId ) SELECT id,title,date,description,amount * 100,categoryId FROM `TransactionOld`")
|
||||
database.execSQL("UPDATE `Transaction` SET isExpense = 1 WHERE id IN (SELECT id FROM `TransactionOld` WHERE `TransactionOld`.type = 'EXPENSE')")
|
||||
database.execSQL("UPDATE `Transaction` SET isExpense = 0 WHERE id IN (SELECT id FROM `TransactionOld` WHERE `TransactionOld`.type = 'INCOME')")
|
||||
database.execSQL("DROP TABLE `TransactionOld`")
|
||||
|
||||
database.execSQL("ALTER TABLE `Category` RENAME TO `CategoryOld`")
|
||||
database.execSQL("CREATE TABLE `Category` (`id` INTEGER,`remoteId` TEXT,`name` TEXT NOT NULL, `amount` INTEGER NOT NULL, `repeat` TEXT, `color` INTEGER NOT NULL DEFAULT 0,PRIMARY KEY (`id`))")
|
||||
database.execSQL("INSERT INTO `Category` ( id, name, amount, repeat, color ) SELECT id,name,amount * 100,repeat,color FROM `CategoryOld`")
|
||||
database.execSQL("DROP TABLE `CategoryOld`")
|
||||
}
|
||||
}
|
|
@ -8,8 +8,9 @@ import android.support.annotation.ColorInt
|
|||
class Category(
|
||||
@PrimaryKey
|
||||
val id: Int?,
|
||||
val remoteId: String?,
|
||||
val name: String,
|
||||
val amount: Double,
|
||||
val amount: Int,
|
||||
val repeat: String?,
|
||||
@ColorInt val color: Int
|
||||
)
|
|
@ -2,17 +2,17 @@ package com.wbrawner.budget.data.model
|
|||
|
||||
import android.arch.persistence.room.Entity
|
||||
import android.arch.persistence.room.PrimaryKey
|
||||
import com.wbrawner.budget.data.TransactionType
|
||||
import java.util.*
|
||||
|
||||
@Entity
|
||||
class Transaction(
|
||||
@PrimaryKey
|
||||
val id: Int?,
|
||||
val title: String,
|
||||
val remoteId: String?,
|
||||
val name: String,
|
||||
val date: Date,
|
||||
val description: String,
|
||||
val amount: Double,
|
||||
val amount: Int,
|
||||
val categoryId: Int?,
|
||||
val type: TransactionType
|
||||
val isExpense: Boolean
|
||||
)
|
|
@ -1,4 +1,4 @@
|
|||
package com.wbrawner.budget.transactions
|
||||
package com.wbrawner.budget.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.support.annotation.StringRes
|
||||
|
@ -6,12 +6,12 @@ import android.support.text.emoji.EmojiCompat
|
|||
import android.support.text.emoji.bundled.BundledEmojiCompatConfig
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import com.wbrawner.budget.R
|
||||
import com.wbrawner.budget.categories.CategoryListFragment
|
||||
import com.wbrawner.budget.data.TransactionType
|
||||
import com.wbrawner.budget.overview.OverviewFragment
|
||||
import com.wbrawner.budget.ui.categories.CategoryListFragment
|
||||
import com.wbrawner.budget.ui.overview.OverviewFragment
|
||||
import com.wbrawner.budget.ui.transactions.TransactionListFragment
|
||||
import kotlinx.android.synthetic.main.activity_transaction_list.*
|
||||
|
||||
class TransactionListActivity : AppCompatActivity() {
|
||||
class MainActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
EmojiCompat.init(BundledEmojiCompatConfig(this))
|
||||
|
@ -20,9 +20,14 @@ class TransactionListActivity : AppCompatActivity() {
|
|||
|
||||
menu_main.setOnNavigationItemSelectedListener { item ->
|
||||
when (item.itemId) {
|
||||
R.id.action_expenses -> updateFragment(TransactionType.EXPENSE)
|
||||
R.id.action_income -> updateFragment(TransactionType.INCOME)
|
||||
R.id.action_categories -> updateFragment(CategoryListFragment.TAG_FRAGMENT, CategoryListFragment.TITLE_FRAGMENT)
|
||||
R.id.action_transactions -> updateFragment(
|
||||
TransactionListFragment.TAG_FRAGMENT,
|
||||
TransactionListFragment.TITLE_FRAGMENT
|
||||
)
|
||||
R.id.action_categories -> updateFragment(
|
||||
CategoryListFragment.TAG_FRAGMENT,
|
||||
CategoryListFragment.TITLE_FRAGMENT
|
||||
)
|
||||
else ->
|
||||
updateFragment(OverviewFragment.TAG_FRAGMENT, OverviewFragment.TITLE_FRAGMENT)
|
||||
}
|
||||
|
@ -31,10 +36,6 @@ class TransactionListActivity : AppCompatActivity() {
|
|||
menu_main.selectedItemId = R.id.action_overview
|
||||
}
|
||||
|
||||
private fun updateFragment(type: TransactionType) {
|
||||
updateFragment(type.name, type.title)
|
||||
}
|
||||
|
||||
private fun updateFragment(tag: String, @StringRes title: Int) {
|
||||
setTitle(title)
|
||||
var fragment = supportFragmentManager.findFragmentByTag(tag)
|
||||
|
@ -44,11 +45,7 @@ class TransactionListActivity : AppCompatActivity() {
|
|||
OverviewFragment.TAG_FRAGMENT -> OverviewFragment()
|
||||
CategoryListFragment.TAG_FRAGMENT -> CategoryListFragment()
|
||||
else -> {
|
||||
TransactionListFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putSerializable(TransactionListFragment.ARG_TYPE, TransactionType.valueOf(tag))
|
||||
}
|
||||
}
|
||||
TransactionListFragment()
|
||||
}
|
||||
}
|
||||
ft.add(R.id.content_container, fragment, tag)
|
|
@ -1,4 +1,4 @@
|
|||
package com.wbrawner.budget.categories
|
||||
package com.wbrawner.budget.ui.categories
|
||||
|
||||
import android.arch.lifecycle.Observer
|
||||
import android.arch.lifecycle.ViewModelProviders
|
||||
|
@ -7,6 +7,7 @@ import android.os.Bundle
|
|||
import android.support.v7.app.AppCompatActivity
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import com.wbrawner.budget.R
|
||||
import com.wbrawner.budget.data.model.Category
|
||||
import kotlinx.android.synthetic.main.activity_add_edit_category.*
|
||||
|
@ -28,6 +29,7 @@ class AddEditCategoryActivity : AppCompatActivity() {
|
|||
return
|
||||
}
|
||||
|
||||
|
||||
viewModel.getCategory(intent!!.extras!!.getInt(EXTRA_CATEGORY_ID))
|
||||
.observe(this, Observer<Category> { category ->
|
||||
if (category == null) {
|
||||
|
@ -38,7 +40,7 @@ class AddEditCategoryActivity : AppCompatActivity() {
|
|||
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))
|
||||
edit_category_amount.setText(String.format("%.02f", category.amount / 100.0f))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -58,8 +60,9 @@ class AddEditCategoryActivity : AppCompatActivity() {
|
|||
if (!validateFields()) return true
|
||||
viewModel.saveCategory(Category(
|
||||
id = id,
|
||||
remoteId = null,
|
||||
name = edit_category_name.text.toString(),
|
||||
amount = edit_category_amount.text.toString().toDouble(),
|
||||
amount = edit_category_amount.rawValue.toInt(),
|
||||
color = Color.parseColor("#FF0000"),
|
||||
repeat = "never"
|
||||
))
|
|
@ -1,4 +1,4 @@
|
|||
package com.wbrawner.budget.categories
|
||||
package com.wbrawner.budget.ui.categories
|
||||
|
||||
import android.arch.lifecycle.LifecycleOwner
|
||||
import android.arch.lifecycle.Observer
|
||||
|
@ -11,7 +11,7 @@ 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.ui.categories.AddEditCategoryActivity.Companion.EXTRA_CATEGORY_ID
|
||||
import com.wbrawner.budget.data.model.Category
|
||||
|
||||
class CategoryAdapter(
|
||||
|
@ -30,18 +30,22 @@ class CategoryAdapter(
|
|||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val category = data[position]
|
||||
holder.title?.text = category.name
|
||||
holder.amount?.text = String.format("${'$'}%.02f", category.amount)
|
||||
holder.amount?.text = String.format("${'$'}%.02f", category.amount / 100.0f)
|
||||
viewModel.getCurrentBalance(category.id!!)
|
||||
.observe(lifecycleOwner, Observer<Double> { balance ->
|
||||
.observe(lifecycleOwner, Observer<Int> { balance ->
|
||||
holder.progress?.isIndeterminate = false
|
||||
if (balance == null) {
|
||||
holder.progress?.progress = 0
|
||||
} else {
|
||||
holder.progress?.max = category.amount.toInt()
|
||||
holder.progress?.max = category.amount
|
||||
holder.progress?.setProgress(
|
||||
Math.abs(balance).toInt(),
|
||||
-1 * balance,
|
||||
true
|
||||
)
|
||||
holder.amount?.text = holder.itemView.context.getString(
|
||||
R.string.balance_remaning,
|
||||
(category.amount + balance) / 100.0f
|
||||
)
|
||||
}
|
||||
})
|
||||
holder.itemView.setOnClickListener {
|
|
@ -1,4 +1,4 @@
|
|||
package com.wbrawner.budget.categories
|
||||
package com.wbrawner.budget.ui.categories
|
||||
|
||||
import android.arch.lifecycle.Observer
|
||||
import android.arch.lifecycle.ViewModelProviders
|
||||
|
@ -41,7 +41,7 @@ class CategoryListFragment : Fragment() {
|
|||
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")
|
||||
noDataView?.setText(R.string.categories_no_data)
|
||||
recyclerView?.visibility = View.GONE
|
||||
noDataView?.visibility = View.VISIBLE
|
||||
} else {
|
|
@ -1,4 +1,4 @@
|
|||
package com.wbrawner.budget.categories
|
||||
package com.wbrawner.budget.ui.categories
|
||||
|
||||
import android.app.Application
|
||||
import android.arch.lifecycle.AndroidViewModel
|
|
@ -1,4 +1,4 @@
|
|||
package com.wbrawner.budget.overview
|
||||
package com.wbrawner.budget.ui.overview
|
||||
|
||||
import android.arch.lifecycle.Observer
|
||||
import android.arch.lifecycle.ViewModelProviders
|
||||
|
@ -9,7 +9,7 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import com.wbrawner.budget.R
|
||||
import com.wbrawner.budget.transactions.TransactionViewModel
|
||||
import com.wbrawner.budget.ui.transactions.TransactionViewModel
|
||||
|
||||
class OverviewFragment : Fragment() {
|
||||
lateinit var viewModel: TransactionViewModel
|
||||
|
@ -32,15 +32,15 @@ class OverviewFragment : Fragment() {
|
|||
}
|
||||
|
||||
viewModel.getCurrentBalance().observe(this, Observer { balance ->
|
||||
val safeBalance: Double = balance?: 0.0
|
||||
val safeBalance: Int = balance?: 0
|
||||
val balanceView = view.findViewById<TextView>(R.id.overview_current_balance)
|
||||
val color = when {
|
||||
safeBalance > 0.0 -> R.color.colorTextGreen
|
||||
safeBalance == 0.0 -> R.color.colorTextPrimary
|
||||
safeBalance > 0 -> R.color.colorTextGreen
|
||||
safeBalance == 0 -> R.color.colorTextPrimary
|
||||
else -> R.color.colorTextRed
|
||||
}
|
||||
balanceView.setTextColor(resources.getColor(color, activity!!.theme))
|
||||
balanceView.text = String.format("${'$'}%.02f", safeBalance)
|
||||
balanceView.text = String.format("${'$'}%.02f", safeBalance / 100.0f)
|
||||
})
|
||||
showData(view, true)
|
||||
})
|
|
@ -1,4 +1,4 @@
|
|||
package com.wbrawner.budget.transactions
|
||||
package com.wbrawner.budget.ui.transactions
|
||||
|
||||
import android.arch.lifecycle.Observer
|
||||
import android.arch.lifecycle.ViewModelProviders
|
||||
|
@ -10,14 +10,12 @@ import android.widget.ArrayAdapter
|
|||
import com.wbrawner.budget.R
|
||||
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
|
||||
import kotlinx.android.synthetic.main.activity_add_edit_transaction.*
|
||||
import java.util.*
|
||||
|
||||
class AddEditTransactionActivity : AppCompatActivity() {
|
||||
lateinit var viewModel: TransactionViewModel
|
||||
lateinit var type: TransactionType
|
||||
var id: Int? = null
|
||||
var date: Date = Date()
|
||||
var menu: Menu? = null
|
||||
|
@ -27,6 +25,8 @@ class AddEditTransactionActivity : AppCompatActivity() {
|
|||
setContentView(R.layout.activity_add_edit_transaction)
|
||||
setSupportActionBar(action_bar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
setTitle(R.string.title_add_transaction)
|
||||
edit_transaction_type_expense.isChecked = true
|
||||
viewModel = ViewModelProviders.of(this).get(TransactionViewModel::class.java)
|
||||
viewModel.getCategories()
|
||||
.observe(this, Observer<List<TransactionCategory>> { categories ->
|
||||
|
@ -44,13 +44,8 @@ class AddEditTransactionActivity : AppCompatActivity() {
|
|||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||
edit_transaction_category.adapter = adapter
|
||||
})
|
||||
if (intent?.hasExtra(EXTRA_TYPE) == true) {
|
||||
type = TransactionType.valueOf(intent?.extras?.getString(EXTRA_TYPE, "EXPENSE")
|
||||
?: "EXPENSE")
|
||||
setTitle(type.addTitle)
|
||||
return
|
||||
} else if (intent?.hasExtra(EXTRA_TRANSACTION_ID) != true) {
|
||||
finish()
|
||||
|
||||
if (intent?.hasExtra(EXTRA_TRANSACTION_ID) != true) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -62,12 +57,15 @@ class AddEditTransactionActivity : AppCompatActivity() {
|
|||
}
|
||||
val transaction = transactionWithCategory.transaction
|
||||
id = transaction.id
|
||||
type = transaction.type
|
||||
setTitle(type.editTitle)
|
||||
menu?.findItem(R.id.action_delete)?.isVisible = true
|
||||
edit_transaction_title.setText(transaction.title)
|
||||
edit_transaction_title.setText(transaction.name)
|
||||
edit_transaction_description.setText(transaction.description)
|
||||
edit_transaction_amount.setText(String.format("%.02f", transaction.amount))
|
||||
edit_transaction_amount.setText(String.format("%.02f", transaction.amount / 100.0f))
|
||||
if (transaction.isExpense) {
|
||||
edit_transaction_type_expense.isChecked = true
|
||||
} else {
|
||||
edit_transaction_type_income.isChecked = true
|
||||
}
|
||||
val field = Calendar.getInstance()
|
||||
field.time = transaction.date
|
||||
val year = field.get(Calendar.YEAR)
|
||||
|
@ -105,12 +103,13 @@ class AddEditTransactionActivity : AppCompatActivity() {
|
|||
|
||||
viewModel.saveTransaction(Transaction(
|
||||
id = id,
|
||||
title = edit_transaction_title.text.toString(),
|
||||
name = edit_transaction_title.text.toString(),
|
||||
date = cal.time,
|
||||
description = edit_transaction_description.text.toString(),
|
||||
amount = edit_transaction_amount.text.toString().toDouble(),
|
||||
type = type,
|
||||
categoryId = (edit_transaction_category.selectedItem as TransactionCategory).id
|
||||
amount = edit_transaction_amount.rawValue.toInt(),
|
||||
isExpense = edit_transaction_type_expense.isChecked,
|
||||
categoryId = (edit_transaction_category.selectedItem as TransactionCategory).id,
|
||||
remoteId = null
|
||||
))
|
||||
finish()
|
||||
}
|
||||
|
@ -124,7 +123,6 @@ class AddEditTransactionActivity : AppCompatActivity() {
|
|||
|
||||
|
||||
companion object {
|
||||
const val EXTRA_TYPE = "EXTRA_TRANSACTION_TYPE"
|
||||
const val EXTRA_TRANSACTION_ID = "EXTRA_TRANSACTION_ID"
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.wbrawner.budget.transactions
|
||||
package com.wbrawner.budget.ui.transactions
|
||||
|
||||
import android.content.Intent
|
||||
import android.support.v4.content.ContextCompat.startActivity
|
||||
|
@ -8,7 +8,7 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import com.wbrawner.budget.R
|
||||
import com.wbrawner.budget.transactions.AddEditTransactionActivity.Companion.EXTRA_TRANSACTION_ID
|
||||
import com.wbrawner.budget.ui.transactions.AddEditTransactionActivity.Companion.EXTRA_TRANSACTION_ID
|
||||
import com.wbrawner.budget.data.model.Transaction
|
||||
import com.wbrawner.budget.data.model.TransactionWithCategory
|
||||
import java.text.SimpleDateFormat
|
||||
|
@ -29,13 +29,12 @@ class TransactionAdapter() : RecyclerView.Adapter<TransactionAdapter.ViewHolder>
|
|||
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.name
|
||||
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 / 100.0f)
|
||||
val context = holder.itemView.context
|
||||
holder.amount.setTextColor(
|
||||
context.resources.getColor(transaction.type.textColor, context.theme)
|
||||
)
|
||||
val color = if (transaction.isExpense) R.color.colorTextRed else R.color.colorTextGreen
|
||||
holder.amount.setTextColor(context.resources.getColor(color, context.theme))
|
||||
holder.itemView.setOnClickListener {
|
||||
startActivity(
|
||||
it.context.applicationContext,
|
|
@ -1,4 +1,4 @@
|
|||
package com.wbrawner.budget.transactions
|
||||
package com.wbrawner.budget.ui.transactions
|
||||
|
||||
import android.arch.lifecycle.Observer
|
||||
import android.arch.lifecycle.ViewModelProviders
|
||||
|
@ -13,13 +13,10 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.wbrawner.budget.R
|
||||
import com.wbrawner.budget.transactions.AddEditTransactionActivity.Companion.EXTRA_TYPE
|
||||
import com.wbrawner.budget.data.TransactionType
|
||||
import com.wbrawner.budget.data.model.TransactionWithCategory
|
||||
|
||||
class TransactionListFragment : Fragment() {
|
||||
lateinit var viewModel: TransactionViewModel
|
||||
lateinit var type: TransactionType
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -31,8 +28,6 @@ class TransactionListFragment : Fragment() {
|
|||
return
|
||||
}
|
||||
|
||||
type = arguments?.getSerializable(ARG_TYPE) as? TransactionType ?: TransactionType.EXPENSE
|
||||
|
||||
viewModel = ViewModelProviders.of(activity!!).get(TransactionViewModel::class.java)
|
||||
}
|
||||
|
||||
|
@ -41,12 +36,12 @@ class TransactionListFragment : Fragment() {
|
|||
val recyclerView = view.findViewById<RecyclerView>(R.id.list_transactions)
|
||||
val fab = view.findViewById<FloatingActionButton>(R.id.fab_add_transaction)
|
||||
recyclerView.layoutManager = LinearLayoutManager(activity)
|
||||
viewModel.getTransactionsByType(20, type)
|
||||
viewModel.getTransactions(20)
|
||||
.observe(this, Observer<List<TransactionWithCategory>> { data ->
|
||||
val noDataView = view.findViewById<EmojiTextView>(R.id.transaction_list_no_data)
|
||||
if (data == null || data.isEmpty()) {
|
||||
recyclerView.adapter = null
|
||||
noDataView?.setText(type.noDataText)
|
||||
noDataView?.setText(R.string.transactions_no_data)
|
||||
recyclerView?.visibility = View.GONE
|
||||
noDataView?.visibility = View.VISIBLE
|
||||
} else {
|
||||
|
@ -62,9 +57,7 @@ class TransactionListFragment : Fragment() {
|
|||
})
|
||||
fab.setOnClickListener {
|
||||
startActivity(
|
||||
Intent(activity, AddEditTransactionActivity::class.java).apply {
|
||||
this.putExtra(EXTRA_TYPE, this@TransactionListFragment.type.name)
|
||||
}
|
||||
Intent(activity, AddEditTransactionActivity::class.java)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -72,6 +65,7 @@ class TransactionListFragment : Fragment() {
|
|||
}
|
||||
|
||||
companion object {
|
||||
const val ARG_TYPE = "TRANSACTION_TYPE"
|
||||
const val TAG_FRAGMENT = "transactions"
|
||||
const val TITLE_FRAGMENT = R.string.title_transactions
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.wbrawner.budget.transactions
|
||||
package com.wbrawner.budget.ui.transactions
|
||||
|
||||
import android.app.Application
|
||||
import android.arch.lifecycle.AndroidViewModel
|
||||
|
@ -16,10 +16,10 @@ class TransactionViewModel(application: Application): AndroidViewModel(applicati
|
|||
|
||||
fun getTransactions(count: Int): LiveData<List<TransactionWithCategory>> = transactionRepo.getTransactions(count)
|
||||
|
||||
fun getTransactionsByType(count: Int, type: TransactionType): LiveData<List<TransactionWithCategory>>
|
||||
= transactionRepo.getTransactionsByType(count, type)
|
||||
// fun getTransactionsByType(count: Int, type: TransactionType): LiveData<List<TransactionWithCategory>>
|
||||
// = transactionRepo.getTransactionsByType(count, type)
|
||||
|
||||
fun getCurrentBalance(): LiveData<Double> = transactionRepo.getCurrentBalance()
|
||||
fun getCurrentBalance(): LiveData<Int> = transactionRepo.getCurrentBalance()
|
||||
|
||||
fun getCategories(): LiveData<List<TransactionCategory>> = transactionRepo.getCategories()
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
<?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"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
@ -51,10 +52,12 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/container_edit_category_name">
|
||||
|
||||
<android.support.design.widget.TextInputEditText
|
||||
<com.blackcat.currencyedittext.CurrencyEditText
|
||||
android:id="@+id/edit_category_amount"
|
||||
style="@style/AppTheme.EditText"
|
||||
android:inputType="numberDecimal" />
|
||||
android:text="0.00"
|
||||
android:inputType="number"
|
||||
tools:ignore="HardcodedText" />
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
|
||||
<!--<TextView-->
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
android:id="@+id/container_edit_transaction_title"
|
||||
style="@style/AppTheme.EditText.Container"
|
||||
android:hint="@string/prompt_transaction_title"
|
||||
app:layout_constraintBottom_toTopOf="@+id/container_edit_transaction_description"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
@ -46,7 +45,6 @@
|
|||
android:id="@+id/container_edit_transaction_description"
|
||||
style="@style/AppTheme.EditText.Container"
|
||||
android:hint="@string/prompt_transaction_description"
|
||||
app:layout_constraintBottom_toTopOf="@+id/container_edit_transaction_amount"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/container_edit_transaction_title">
|
||||
|
@ -62,17 +60,36 @@
|
|||
android:id="@+id/container_edit_transaction_amount"
|
||||
style="@style/AppTheme.EditText.Container"
|
||||
android:hint="@string/prompt_transaction_amount"
|
||||
app:layout_constraintBottom_toTopOf="@+id/container_edit_transaction_date"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/container_edit_transaction_description">
|
||||
|
||||
<android.support.design.widget.TextInputEditText
|
||||
<com.blackcat.currencyedittext.CurrencyEditText
|
||||
android:id="@+id/edit_transaction_amount"
|
||||
style="@style/AppTheme.EditText"
|
||||
android:inputType="numberDecimal" />
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/container_edit_transaction_type"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintTop_toBottomOf="@+id/container_edit_transaction_amount">
|
||||
<RadioButton
|
||||
android:id="@+id/edit_transaction_type_expense"
|
||||
android:text="@string/type_expense"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
<RadioButton
|
||||
android:id="@+id/edit_transaction_type_income"
|
||||
android:text="@string/type_income"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
</RadioGroup>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/container_edit_transaction_date"
|
||||
style="@style/AppTheme.EditText.Hint"
|
||||
|
@ -80,15 +97,13 @@
|
|||
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" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/container_edit_transaction_type" />
|
||||
|
||||
<DatePicker
|
||||
android:id="@+id/edit_transaction_date"
|
||||
style="@style/AppTheme.DatePicker"
|
||||
app:layout_constraintBottom_toTopOf="@+id/container_edit_transaction_category"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/container_edit_transaction_date" />
|
||||
|
@ -100,7 +115,6 @@
|
|||
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" />
|
||||
|
@ -109,7 +123,6 @@
|
|||
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" />
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.wbrawner.budget.transactions.TransactionListActivity">
|
||||
tools:context=".ui.MainActivity">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/action_bar"
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
android:id="@+id/category_amount"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="18sp"
|
||||
style="?android:attr/textAppearanceSmall"
|
||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||
app:layout_constraintBottom_toTopOf="@id/barrier"
|
||||
app:layout_constraintStart_toEndOf="@+id/category_title"
|
||||
|
|
|
@ -5,13 +5,9 @@
|
|||
android:icon="@drawable/ic_baseline_dashboard_24dp"
|
||||
android:title="@string/title_dashboard" />
|
||||
<item
|
||||
android:id="@+id/action_expenses"
|
||||
android:icon="@drawable/ic_money_off_black_24dp"
|
||||
android:title="@string/title_expenses" />
|
||||
<item
|
||||
android:id="@+id/action_income"
|
||||
android:id="@+id/action_transactions"
|
||||
android:icon="@drawable/ic_attach_money_black_24dp"
|
||||
android:title="@string/title_income" />
|
||||
android:title="@string/title_transactions" />
|
||||
<item
|
||||
android:id="@+id/action_categories"
|
||||
android:icon="@drawable/ic_baseline_category_24px"
|
||||
|
|
|
@ -2,23 +2,20 @@
|
|||
<string name="app_name">My Allowance</string>
|
||||
<string name="app_name_short">Allowance</string>
|
||||
<string name="prompt_transaction_title">Title</string>
|
||||
<string name="title_add_income">Add Income</string>
|
||||
<string name="title_add_expense">Add Expense</string>
|
||||
<string name="title_add_transaction">Add Transaction</string>
|
||||
<string name="prompt_transaction_description">Description</string>
|
||||
<string name="prompt_transaction_date">Date</string>
|
||||
<string name="prompt_transaction_amount">Amount</string>
|
||||
<string name="title_expenses">Expenses</string>
|
||||
<string name="title_income">Income</string>
|
||||
<string name="type_expense">Expense</string>
|
||||
<string name="type_income">Income</string>
|
||||
<string name="title_dashboard">Dashboard</string>
|
||||
<string name="label_current_balance">Current Balance</string>
|
||||
<string name="overview_no_data">📈\nAdd some transactions to see an overview of your spending here</string>
|
||||
<string name="action_save">Save</string>
|
||||
<string name="action_delete">Delete</string>
|
||||
<string name="title_edit_income">Edit Income</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="expenses_no_data">🤔\nAre you sure you haven\'t spent any money?</string>
|
||||
<string name="title_edit_transaction">Edit Transaction</string>
|
||||
<string name="categories_no_data">🌳\nGet to work! Money doesn\'t grow on trees after all</string>
|
||||
<string name="transactions_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>
|
||||
|
@ -28,4 +25,6 @@
|
|||
<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>
|
||||
<string name="title_transactions">Transactions</string>
|
||||
<string name="balance_remaning">\$%1$.02f remaining</string>
|
||||
</resources>
|
||||
|
|
|
@ -2,33 +2,15 @@
|
|||
<shortcuts xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<shortcut
|
||||
android:shortcutId="expense"
|
||||
android:enabled="true"
|
||||
android:icon="@drawable/ic_shortcut_money_off"
|
||||
android:shortcutShortLabel="@string/title_add_expense"
|
||||
tools:ignore="UnusedAttribute">
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
android:targetPackage="com.wbrawner.budget"
|
||||
android:targetClass="com.wbrawner.budget.transactions.AddEditTransactionActivity">
|
||||
<extra
|
||||
android:name="EXTRA_TRANSACTION_TYPE"
|
||||
android:value="EXPENSE" />
|
||||
</intent>
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:shortcutId="income"
|
||||
android:shortcutId="transaction"
|
||||
android:enabled="true"
|
||||
android:icon="@drawable/ic_shortcut_attach_money"
|
||||
android:shortcutShortLabel="@string/title_add_income"
|
||||
android:shortcutShortLabel="@string/title_add_transaction"
|
||||
tools:ignore="UnusedAttribute">
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
android:targetPackage="com.wbrawner.budget"
|
||||
android:targetClass="com.wbrawner.budget.transactions.AddEditTransactionActivity">
|
||||
<extra
|
||||
android:name="EXTRA_TRANSACTION_TYPE"
|
||||
android:value="INCOME" />
|
||||
android:targetClass="com.wbrawner.budget.ui.transactions.AddEditTransactionActivity">
|
||||
</intent>
|
||||
</shortcut>
|
||||
</shortcuts>
|
|
@ -19,6 +19,7 @@ allprojects {
|
|||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven { url "https://jitpack.io" }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue