WIP: Migrate transaction form to compose
This commit is contained in:
parent
16b1d56be2
commit
16b4823450
6 changed files with 367 additions and 95 deletions
|
@ -80,15 +80,4 @@ dependencies {
|
|||
implementation 'androidx.activity:activity-compose:1.3.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha07'
|
||||
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose"
|
||||
|
||||
debugImplementation "com.willowtreeapps.hyperion:hyperion-core:$hyperion"
|
||||
debugImplementation "com.willowtreeapps.hyperion:hyperion-attr:$hyperion"
|
||||
debugImplementation "com.willowtreeapps.hyperion:hyperion-build-config:$hyperion"
|
||||
debugImplementation "com.willowtreeapps.hyperion:hyperion-crash:$hyperion"
|
||||
debugImplementation "com.willowtreeapps.hyperion:hyperion-disk:$hyperion"
|
||||
debugImplementation "com.willowtreeapps.hyperion:hyperion-geiger-counter:$hyperion"
|
||||
debugImplementation "com.willowtreeapps.hyperion:hyperion-measurement:$hyperion"
|
||||
debugImplementation "com.willowtreeapps.hyperion:hyperion-phoenix:$hyperion"
|
||||
debugImplementation "com.willowtreeapps.hyperion:hyperion-recorder:$hyperion"
|
||||
debugImplementation "com.willowtreeapps.hyperion:hyperion-shared-preferences:$hyperion"
|
||||
}
|
||||
|
|
|
@ -16,10 +16,10 @@
|
|||
tools:targetApi="n">
|
||||
<activity
|
||||
android:name=".ui.auth.AuthActivity"
|
||||
android:theme="@style/Theme.App.Starting"
|
||||
android:exported="true"
|
||||
android:resizeableActivity="true"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:exported="true">
|
||||
android:theme="@style/Theme.App.Starting"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
|
@ -30,25 +30,29 @@
|
|||
android:resource="@xml/shortcuts" />
|
||||
</activity>
|
||||
|
||||
<activity android:name=".ui.MainActivity"
|
||||
<activity
|
||||
android:name=".ui.MainActivity"
|
||||
android:exported="false"
|
||||
android:resizeableActivity="true"
|
||||
android:theme="@style/AppTheme" />
|
||||
<activity
|
||||
android:name=".ui.transactions.TransactionFormActivity"
|
||||
android:exported="true"
|
||||
android:parentActivityName=".ui.MainActivity"
|
||||
android:resizeableActivity="true"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:theme="@style/AppTheme">
|
||||
android:theme="@style/AppTheme"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".ui.MainActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.categories.CategoryFormActivity"
|
||||
android:exported="false"
|
||||
android:parentActivityName=".ui.MainActivity"
|
||||
android:resizeableActivity="true"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:theme="@style/AppTheme">
|
||||
android:theme="@style/AppTheme"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".ui.MainActivity" />
|
||||
|
|
|
@ -2,15 +2,35 @@ package com.wbrawner.budget.ui
|
|||
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.emoji.text.EmojiCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import com.google.android.material.navigation.NavigationView
|
||||
import com.wbrawner.budget.R
|
||||
import com.wbrawner.budget.common.budget.Budget
|
||||
import com.wbrawner.budget.ui.base.TwigsApp
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import kotlinx.coroutines.flow.collect
|
||||
|
@ -27,7 +47,9 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
|||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
EmojiCompat.init(androidx.emoji.bundled.BundledEmojiCompatConfig(this))
|
||||
setContent {
|
||||
|
||||
}
|
||||
setContentView(R.layout.activity_main)
|
||||
setSupportActionBar(action_bar)
|
||||
toggle = ActionBarDrawerToggle(this, drawerLayout, R.string.action_open, R.string.action_close)
|
||||
|
@ -99,3 +121,74 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
|||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MainScreen() {
|
||||
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
|
||||
val scaffoldState = rememberScaffoldState(drawerState)
|
||||
Scaffold(
|
||||
scaffoldState = scaffoldState,
|
||||
drawerContent = {
|
||||
|
||||
}
|
||||
) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TwigsDrawer(navController: NavController, budgets: List<Budget>) {
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
val image = if (isSystemInDarkTheme()) R.drawable.ic_twigs_outline else R.drawable.ic_twigs_color
|
||||
Image(painter = painterResource(id = image), null)
|
||||
Text(
|
||||
text = "twigs",
|
||||
style = MaterialTheme.typography.h3
|
||||
)
|
||||
}
|
||||
val currentBudget = navController.currentBackStackEntry?.arguments?.getString("id")
|
||||
navController.currentDestination?.arguments?.get()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DrawerItem(@DrawableRes image: Int, text: String, selected: Boolean) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = spacedBy(8.dp, Alignment.Start),
|
||||
) {
|
||||
val tint = if (selected) MaterialTheme.colors.primary else MaterialTheme.colors.onSurface
|
||||
Image(
|
||||
painter = painterResource(id = image),
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(tint)
|
||||
)
|
||||
Text(text = text, color = tint)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun DrawerItem_Preview() {
|
||||
TwigsApp {
|
||||
DrawerItem(R.drawable.ic_folder_open)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun TwigsDrawer_Preview() {
|
||||
val scaffoldState = rememberScaffoldState(rememberDrawerState(initialValue = DrawerValue.Open))
|
||||
TwigsApp {
|
||||
Scaffold(
|
||||
scaffoldState = scaffoldState,
|
||||
drawerContent = { TwigsDrawer() }
|
||||
) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,11 +7,8 @@ import androidx.compose.animation.fadeIn
|
|||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.*
|
||||
|
@ -83,7 +80,6 @@ fun LoginForm(
|
|||
verticalArrangement = spacedBy(8.dp, Alignment.CenterVertically),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
|
||||
Image(
|
||||
painter = painterResource(id = if (isSystemInDarkTheme()) R.drawable.ic_twigs_outline else R.drawable.ic_twigs_color),
|
||||
contentDescription = null
|
||||
|
|
|
@ -7,6 +7,10 @@ import androidx.compose.material.darkColors
|
|||
import androidx.compose.material.lightColors
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.Font
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import com.wbrawner.budget.R
|
||||
|
||||
val lightColors = lightColors(
|
||||
primary = Green500,
|
||||
|
@ -24,6 +28,21 @@ val darkColors = darkColors(
|
|||
fun TwigsTheme(darkMode: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
|
||||
MaterialTheme(
|
||||
colors = if (darkMode) darkColors else lightColors,
|
||||
typography = MaterialTheme.typography.copy(
|
||||
h1 = MaterialTheme.typography.h1.copy(fontFamily = FontFamily(Font(R.font.ubuntu))),
|
||||
h2 = MaterialTheme.typography.h2.copy(fontFamily = FontFamily(Font(R.font.ubuntu))),
|
||||
h3 = MaterialTheme.typography.h3.copy(fontFamily = FontFamily(Font(R.font.ubuntu))),
|
||||
h4 = MaterialTheme.typography.h4.copy(fontFamily = FontFamily(Font(R.font.ubuntu))),
|
||||
h5 = MaterialTheme.typography.h5.copy(fontFamily = FontFamily(Font(R.font.ubuntu))),
|
||||
h6 = MaterialTheme.typography.h6.copy(fontFamily = FontFamily(Font(R.font.ubuntu))),
|
||||
subtitle1 = MaterialTheme.typography.subtitle1.copy(fontFamily = FontFamily(Font(R.font.ubuntu))),
|
||||
subtitle2 = MaterialTheme.typography.subtitle2.copy(fontFamily = FontFamily(Font(R.font.ubuntu))),
|
||||
body1 = MaterialTheme.typography.body1.copy(fontFamily = FontFamily(Font(R.font.ubuntu))),
|
||||
body2 = MaterialTheme.typography.body2.copy(fontFamily = FontFamily(Font(R.font.ubuntu))),
|
||||
button = MaterialTheme.typography.button.copy(fontFamily = FontFamily(Font(R.font.ubuntu))),
|
||||
caption = MaterialTheme.typography.caption.copy(fontFamily = FontFamily(Font(R.font.ubuntu))),
|
||||
overline = MaterialTheme.typography.overline.copy(fontFamily = FontFamily(Font(R.font.ubuntu))),
|
||||
),
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
|
|
@ -12,6 +12,23 @@ import android.widget.AdapterView
|
|||
import android.widget.ArrayAdapter
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.Devices
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.app.NavUtils
|
||||
import androidx.core.app.TaskStackBuilder
|
||||
import com.google.android.material.datepicker.MaterialDatePicker
|
||||
|
@ -21,6 +38,7 @@ import com.wbrawner.budget.common.category.Category
|
|||
import com.wbrawner.budget.common.transaction.Transaction
|
||||
import com.wbrawner.budget.ui.EXTRA_TRANSACTION_ID
|
||||
import com.wbrawner.budget.ui.MainActivity
|
||||
import com.wbrawner.budget.ui.base.TwigsApp
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.android.synthetic.main.activity_add_edit_transaction.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
@ -265,3 +283,156 @@ class TransactionFormActivity : AppCompatActivity(), CoroutineScope {
|
|||
}
|
||||
|
||||
fun Editable?.toLong(): Long = toString().toDouble().toLong() * 100
|
||||
|
||||
@Composable
|
||||
fun TransactionForm(
|
||||
title: String,
|
||||
setTitle: (String) -> Unit,
|
||||
description: String,
|
||||
setDescription: (String) -> Unit,
|
||||
amount: String,
|
||||
setAmount: (String) -> Unit,
|
||||
expense: Boolean,
|
||||
setExpense: (Boolean) -> Unit,
|
||||
date: Long,
|
||||
setDate: (Long) -> Unit,
|
||||
budget: Budget,
|
||||
setBudget: (Budget) -> Unit,
|
||||
budgets: List<Budget>,
|
||||
category: Category,
|
||||
setCategory: (Category) -> Unit,
|
||||
categories: List<Category>,
|
||||
) {
|
||||
val scrollState = rememberScrollState()
|
||||
val (budgetsExpanded, setBudgetsExpanded) = remember { mutableStateOf(false) }
|
||||
val (categoriesExpanded, setCategoriesExpanded) = remember { mutableStateOf(false) }
|
||||
val context = LocalContext.current
|
||||
val formattedDate = remember(date) { DateFormat.getDateFormat(context).format(Date(date)) }
|
||||
val formattedTime = remember(date) { DateFormat.getTimeFormat(context).format(Date(date)) }
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp)
|
||||
.verticalScroll(scrollState),
|
||||
verticalArrangement = spacedBy(8.dp, Alignment.CenterVertically)
|
||||
) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = title,
|
||||
onValueChange = setTitle,
|
||||
placeholder = { Text("Title") },
|
||||
maxLines = 1
|
||||
)
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = description,
|
||||
onValueChange = setDescription,
|
||||
placeholder = { Text("Description") }
|
||||
)
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = amount,
|
||||
onValueChange = setAmount,
|
||||
placeholder = { Text("Amount") },
|
||||
maxLines = 1,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number)
|
||||
)
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
RadioButton(selected = expense, onClick = { setExpense(true) })
|
||||
Text("Expense")
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
RadioButton(selected = !expense, onClick = { setExpense(false) })
|
||||
Text("Income")
|
||||
}
|
||||
Text(text = "Date", style = MaterialTheme.typography.caption)
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
TextButton(
|
||||
onClick = { /*TODO: Show date picker dialog */ },
|
||||
colors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colors.onSurface)
|
||||
) {
|
||||
Text(formattedDate)
|
||||
}
|
||||
TextButton(
|
||||
onClick = { /*TODO: Show time picker dialog */ },
|
||||
colors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colors.onSurface)
|
||||
) {
|
||||
Text(formattedTime)
|
||||
}
|
||||
}
|
||||
Spinner("Budget", budget.name, { it.name }, budgets, setBudget, budgetsExpanded, setBudgetsExpanded)
|
||||
Spinner("Categories", category.title, { it.title }, categories, setCategory, categoriesExpanded, setCategoriesExpanded)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun <T> Spinner(
|
||||
title: String,
|
||||
selectedItemTitle: String,
|
||||
itemTitle: (T) -> String,
|
||||
items: List<T>,
|
||||
selectItem: (T) -> Unit,
|
||||
expanded: Boolean,
|
||||
setExpanded: (Boolean) -> Unit
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
Text(text = title, style = MaterialTheme.typography.caption)
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = selectedItemTitle,
|
||||
onValueChange = {},
|
||||
readOnly = true
|
||||
)
|
||||
DropdownMenu(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
expanded = expanded,
|
||||
onDismissRequest = { setExpanded(false) }
|
||||
) {
|
||||
items.forEach {
|
||||
DropdownMenuItem(onClick = { selectItem(it) }) {
|
||||
Text(itemTitle(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun TransactionForm_Preview() {
|
||||
TwigsApp {
|
||||
TransactionForm(
|
||||
title = "",
|
||||
setTitle = { },
|
||||
description = "",
|
||||
setDescription = { },
|
||||
amount = "",
|
||||
setAmount = { },
|
||||
expense = false,
|
||||
setExpense = { },
|
||||
date = System.currentTimeMillis(),
|
||||
setDate = {},
|
||||
budget = Budget(name = "Uncategorized"),
|
||||
setBudget = {},
|
||||
budgets = emptyList(),
|
||||
category = Category("budget", title = "Uncategorized", amount = 0L),
|
||||
setCategory = {},
|
||||
categories = emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(heightDp = 400)
|
||||
fun Spinner_ExpandedPreview() {
|
||||
TwigsApp {
|
||||
Spinner(
|
||||
"Items",
|
||||
"Uncategorized",
|
||||
{ it },
|
||||
listOf("one", "two", "three"),
|
||||
{},
|
||||
true,
|
||||
{}
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue