Moved to complete Compose layout, rewrote all composables, removed all xml layouts. Changed navigation component.
This commit is contained in:
@@ -67,39 +67,46 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'androidx.room:room-runtime:2.4.2'
|
implementation 'androidx.room:room-runtime:2.4.2'
|
||||||
implementation "androidx.compose.ui:ui:1.3.0-alpha01"
|
|
||||||
implementation "androidx.compose.ui:ui-tooling-preview:1.3.0-alpha01"
|
|
||||||
implementation 'androidx.compose.material:material-icons-extended:1.3.0-alpha01'
|
|
||||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.0'
|
|
||||||
kapt 'androidx.room:room-compiler:2.4.2'
|
|
||||||
|
|
||||||
implementation 'androidx.room:room-rxjava3:2.4.2'
|
implementation 'androidx.room:room-rxjava3:2.4.2'
|
||||||
implementation "androidx.room:room-paging:2.4.2"
|
implementation "androidx.room:room-paging:2.4.2"
|
||||||
implementation "androidx.room:room-ktx:2.4.2"
|
implementation "androidx.room:room-ktx:2.4.2"
|
||||||
|
kapt 'androidx.room:room-compiler:2.4.2'
|
||||||
|
|
||||||
|
implementation "androidx.compose.ui:ui:1.3.0-alpha01"
|
||||||
|
implementation "androidx.compose.ui:ui-tooling-preview:1.3.0-alpha01"
|
||||||
|
implementation 'androidx.compose.material:material-icons-extended:1.3.0-alpha01'
|
||||||
|
implementation 'androidx.compose.material:material:1.3.0-alpha01'
|
||||||
|
implementation 'androidx.compose.runtime:runtime-livedata:1.1.1'
|
||||||
|
implementation 'androidx.compose.animation:animation:1.3.0-alpha01'
|
||||||
|
implementation 'androidx.compose.ui:ui-tooling:1.3.0-alpha01'
|
||||||
|
androidTestImplementation 'androidx.compose.ui:ui-test-junit4:1.3.0-alpha01'
|
||||||
|
debugImplementation "androidx.compose.ui:ui-test-manifest:1.1.1"
|
||||||
|
|
||||||
|
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.0'
|
||||||
|
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.0'
|
||||||
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.0'
|
||||||
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.5.0'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
implementation 'com.google.android.material:material:1.7.0-alpha03'
|
implementation 'com.google.android.material:material:1.7.0-alpha03'
|
||||||
|
implementation 'com.google.android.material:compose-theme-adapter:1.1.14'
|
||||||
|
|
||||||
implementation 'androidx.core:core-ktx:1.8.0'
|
implementation 'androidx.core:core-ktx:1.8.0'
|
||||||
implementation 'androidx.appcompat:appcompat:1.4.2'
|
implementation 'androidx.appcompat:appcompat:1.4.2'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"
|
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"
|
||||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.0'
|
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.0'
|
|
||||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.0'
|
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.0'
|
||||||
implementation 'androidx.navigation:navigation-ui-ktx:2.5.0'
|
implementation 'androidx.navigation:navigation-ui-ktx:2.5.0'
|
||||||
|
implementation "androidx.navigation:navigation-compose:2.5.0"
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
|
|
||||||
implementation 'androidx.activity:activity-compose:1.5.0'
|
implementation 'androidx.activity:activity-compose:1.5.0'
|
||||||
implementation 'androidx.compose.material:material:1.3.0-alpha01'
|
|
||||||
implementation 'androidx.compose.animation:animation:1.3.0-alpha01'
|
|
||||||
implementation 'androidx.compose.ui:ui-tooling:1.3.0-alpha01'
|
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.5.0'
|
|
||||||
implementation 'com.google.android.material:compose-theme-adapter:1.1.14'
|
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'androidx.compose.ui:ui-test-junit4:1.3.0-alpha01'
|
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||||
debugImplementation "androidx.compose.ui:ui-test-manifest:1.1.1"
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 4,
|
||||||
|
"identityHash": "e86f0253ec49cbc67a601f90d5169a8a",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "charges",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `charge` REAL NOT NULL, `battery_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `comment` TEXT NOT NULL DEFAULT '', `created_at` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `synced` INTEGER NOT NULL DEFAULT false, FOREIGN KEY(`battery_id`) REFERENCES `batteries`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "charge",
|
||||||
|
"columnName": "charge",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "batteryId",
|
||||||
|
"columnName": "battery_id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "date",
|
||||||
|
"columnName": "date",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "comment",
|
||||||
|
"columnName": "comment",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "createdAt",
|
||||||
|
"columnName": "created_at",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "CURRENT_TIMESTAMP"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "synced",
|
||||||
|
"columnName": "synced",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "false"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"autoGenerate": true
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_charges_battery_id",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"battery_id"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_charges_battery_id` ON `${TABLE_NAME}` (`battery_id`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "batteries",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"battery_id"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "batteries",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `declared_capacity` REAL DEFAULT NULL, `comment` TEXT NOT NULL DEFAULT '', `synced` INTEGER NOT NULL DEFAULT false, `created_at` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "declaredCapacity",
|
||||||
|
"columnName": "declared_capacity",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": false,
|
||||||
|
"defaultValue": "NULL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "comment",
|
||||||
|
"columnName": "comment",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "synced",
|
||||||
|
"columnName": "synced",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "createdAt",
|
||||||
|
"columnName": "created_at",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "CURRENT_TIMESTAMP"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"autoGenerate": true
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"views": [],
|
||||||
|
"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, 'e86f0253ec49cbc67a601f90d5169a8a')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,16 +13,10 @@
|
|||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.BatteryTracker"
|
android:theme="@style/Theme.BatteryTracker"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
<activity
|
|
||||||
android:name=".AddBattery"
|
|
||||||
android:exported="false"
|
|
||||||
android:label="@string/title_activity_add_battery"
|
|
||||||
android:theme="@style/Theme.BatteryTracker" />
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true">
|
||||||
android:label="@string/app_name"
|
|
||||||
android:theme="@style/Theme.BatteryTracker.NoActionBar">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
|||||||
@@ -1,56 +1,20 @@
|
|||||||
package com.sockenklaus.batterytracker
|
package com.sockenklaus.batterytracker
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import androidx.activity.ComponentActivity
|
||||||
import com.google.android.material.navigation.NavigationView
|
import androidx.activity.compose.setContent
|
||||||
import androidx.navigation.findNavController
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.navigation.ui.AppBarConfiguration
|
import com.sockenklaus.batterytracker.ui.BatteryTracker
|
||||||
import androidx.navigation.ui.navigateUp
|
|
||||||
import androidx.navigation.ui.setupActionBarWithNavController
|
|
||||||
import androidx.navigation.ui.setupWithNavController
|
|
||||||
import androidx.drawerlayout.widget.DrawerLayout
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.navigation.NavController
|
|
||||||
import com.sockenklaus.batterytracker.databinding.ActivityMainBinding
|
|
||||||
import com.sockenklaus.batterytracker.room.BatteryTrackerDB
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
private lateinit var appBarConfiguration: AppBarConfiguration
|
|
||||||
private lateinit var binding: ActivityMainBinding
|
|
||||||
private lateinit var navController: NavController
|
|
||||||
val db: BatteryTrackerDB by lazy { BatteryTrackerDB.getInstance(this) }
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
WindowCompat.setDecorFitsSystemWindows(window, true)
|
||||||
setContentView(binding.root)
|
|
||||||
|
|
||||||
setSupportActionBar(binding.appBarMain.toolbar)
|
setContent {
|
||||||
|
BatteryTracker()
|
||||||
val drawerLayout: DrawerLayout = binding.drawerLayout
|
}
|
||||||
val navView: NavigationView = binding.navView
|
|
||||||
navController = findNavController(R.id.nav_host_fragment_content_main)
|
|
||||||
// Passing each menu ID as a set of Ids because each
|
|
||||||
// menu should be considered as top level destinations.
|
|
||||||
appBarConfiguration = AppBarConfiguration(
|
|
||||||
setOf(
|
|
||||||
R.id.nav_home, R.id.nav_add_charge, R.id.nav_add_battery, R.id.nav_settings
|
|
||||||
), drawerLayout
|
|
||||||
)
|
|
||||||
setupActionBarWithNavController(navController, appBarConfiguration)
|
|
||||||
navView.setupWithNavController(navController)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
|
||||||
// Inflate the menu; this adds items to the action bar if it is present.
|
|
||||||
menuInflater.inflate(R.menu.main, menu)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
|
||||||
val navController = findNavController(R.id.nav_host_fragment_content_main)
|
|
||||||
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,7 @@ import com.sockenklaus.batterytracker.room.entities.Charge
|
|||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
entities = [Charge::class, Battery::class],
|
entities = [Charge::class, Battery::class],
|
||||||
version = 3
|
version = 4
|
||||||
)
|
)
|
||||||
@TypeConverters(Converters::class)
|
@TypeConverters(Converters::class)
|
||||||
abstract class BatteryTrackerDB : RoomDatabase() {
|
abstract class BatteryTrackerDB : RoomDatabase() {
|
||||||
|
|||||||
@@ -2,11 +2,22 @@ package com.sockenklaus.batterytracker.room
|
|||||||
|
|
||||||
import androidx.room.TypeConverter
|
import androidx.room.TypeConverter
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
|
|
||||||
class Converters {
|
class Converters {
|
||||||
|
|
||||||
|
@TypeConverter
|
||||||
|
fun toLocalDate(value: Long): LocalDate {
|
||||||
|
return LocalDate.ofEpochDay(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
@TypeConverter
|
||||||
|
fun toEpochDay(value: LocalDate): Long {
|
||||||
|
return value.toEpochDay()
|
||||||
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun toLocalDateTime(value: Long): LocalDateTime {
|
fun toLocalDateTime(value: Long): LocalDateTime {
|
||||||
return LocalDateTime.ofInstant(Instant.ofEpochMilli(value), ZoneId.systemDefault())
|
return LocalDateTime.ofInstant(Instant.ofEpochMilli(value), ZoneId.systemDefault())
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.sockenklaus.batterytracker.room.entities
|
package com.sockenklaus.batterytracker.room.entities
|
||||||
|
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
@Entity(
|
@Entity(
|
||||||
@@ -24,7 +25,7 @@ data class Charge(
|
|||||||
@ColumnInfo(name = "battery_id")
|
@ColumnInfo(name = "battery_id")
|
||||||
val batteryId: Int,
|
val batteryId: Int,
|
||||||
|
|
||||||
val date: LocalDateTime,
|
val date: LocalDate,
|
||||||
|
|
||||||
@ColumnInfo(defaultValue = "")
|
@ColumnInfo(defaultValue = "")
|
||||||
val comment: String = "",
|
val comment: String = "",
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
package com.sockenklaus.batterytracker.ui
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedContent
|
||||||
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.wrapContentWidth
|
||||||
|
import androidx.compose.material.*
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.*
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import androidx.navigation.compose.NavHost
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
import com.sockenklaus.batterytracker.R
|
||||||
|
import com.sockenklaus.batterytracker.ui.composables.AddBattery
|
||||||
|
import com.sockenklaus.batterytracker.ui.composables.AddCharge
|
||||||
|
import com.sockenklaus.batterytracker.ui.composables.Home
|
||||||
|
import com.sockenklaus.batterytracker.ui.models.MainViewModel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BatteryTracker() {
|
||||||
|
MaterialTheme {
|
||||||
|
val state: MainViewModel = viewModel()
|
||||||
|
state.init()
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
scaffoldState = state.scaffoldState,
|
||||||
|
|
||||||
|
topBar = {
|
||||||
|
if(state.showAppBar){
|
||||||
|
TopAppBar (
|
||||||
|
title = { AppBarTitle(state = state) },
|
||||||
|
navigationIcon = {
|
||||||
|
ToggleDrawerButton(
|
||||||
|
state = state,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
) { innerPadding ->
|
||||||
|
ModalDrawer(
|
||||||
|
drawerState = state.scaffoldState.drawerState,
|
||||||
|
drawerContent = {
|
||||||
|
NavListItem(
|
||||||
|
textId = R.string.nav_home,
|
||||||
|
icon = Icons.Default.Home,
|
||||||
|
route = "home",
|
||||||
|
state = state,
|
||||||
|
)
|
||||||
|
|
||||||
|
NavListItem(
|
||||||
|
icon = Icons.Default.BatteryChargingFull,
|
||||||
|
textId = R.string.nav_add_charge,
|
||||||
|
route = "add_charge",
|
||||||
|
state = state,
|
||||||
|
)
|
||||||
|
|
||||||
|
NavListItem(
|
||||||
|
icon = Icons.Default.BatteryFull,
|
||||||
|
textId = R.string.nav_add_battery,
|
||||||
|
route = "add_battery",
|
||||||
|
state = state,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
NavHost(
|
||||||
|
navController = state.navController,
|
||||||
|
startDestination = "home",
|
||||||
|
modifier = Modifier.padding(innerPadding))
|
||||||
|
{
|
||||||
|
composable("home") { Home(state.navController) }
|
||||||
|
composable("add_charge") { AddCharge(state.navController) }
|
||||||
|
composable("add_battery") { AddBattery(state.navController) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterialApi::class, ExperimentalAnimationApi::class)
|
||||||
|
@Composable
|
||||||
|
fun AppBarTitle(
|
||||||
|
state: MainViewModel
|
||||||
|
){
|
||||||
|
val text = if(state.scaffoldState.drawerState.targetValue == DrawerValue.Closed) {
|
||||||
|
state.appTitle
|
||||||
|
} else {
|
||||||
|
stringResource(R.string.nav_header_title)
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimatedContent(targetState = text) {
|
||||||
|
Text(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@OptIn(ExperimentalAnimationApi::class, ExperimentalMaterialApi::class)
|
||||||
|
fun ToggleDrawerButton(
|
||||||
|
state: MainViewModel
|
||||||
|
){
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
val icon = if(state.scaffoldState.drawerState.targetValue == DrawerValue.Open) {
|
||||||
|
Icons.Default.Close
|
||||||
|
} else {
|
||||||
|
Icons.Default.Menu
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
if(state.scaffoldState.drawerState.isClosed) scope.launch { state.scaffoldState.drawerState.open() }
|
||||||
|
else scope.launch { state.scaffoldState.drawerState.close() }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
AnimatedContent(targetState = icon) { targetState ->
|
||||||
|
Icon(targetState, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
|
@Composable
|
||||||
|
fun NavListItem(
|
||||||
|
icon: ImageVector,
|
||||||
|
textId: Int?,
|
||||||
|
route: String,
|
||||||
|
state: MainViewModel
|
||||||
|
) {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val text = if ( textId != null) stringResource(textId) else ""
|
||||||
|
|
||||||
|
ListItem(
|
||||||
|
icon = { Icon(icon, null) },
|
||||||
|
text = { Text(text) },
|
||||||
|
modifier = Modifier.clickable {
|
||||||
|
scope.launch {
|
||||||
|
state.appTitle = text
|
||||||
|
state.navController.navigate(route)
|
||||||
|
state.scaffoldState.drawerState.close()
|
||||||
|
}
|
||||||
|
}.wrapContentWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package com.sockenklaus.batterytracker.ui.composables
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material.*
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.sockenklaus.batterytracker.R
|
||||||
|
import com.sockenklaus.batterytracker.ui.composables.util.MyOutlinedTextFieldWithSuffix
|
||||||
|
import com.sockenklaus.batterytracker.ui.models.AddBatteryViewModel
|
||||||
|
import com.sockenklaus.batterytracker.util.validateDecimal
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AddBattery(navController: NavController){
|
||||||
|
val model: AddBatteryViewModel = viewModel()
|
||||||
|
val batteries by model.batteries.observeAsState(emptyList())
|
||||||
|
|
||||||
|
val outerPadding = 24.dp
|
||||||
|
val innerPadding = 16.dp
|
||||||
|
|
||||||
|
Column(
|
||||||
|
Modifier.padding(outerPadding)
|
||||||
|
) {
|
||||||
|
|
||||||
|
MyOutlinedTextFieldWithSuffix(
|
||||||
|
value = model.batteryName,
|
||||||
|
onValueChange = { value ->
|
||||||
|
model.batteryName = value
|
||||||
|
model.batteryHasError = false
|
||||||
|
model.batteryHelperId = R.string.helper_required
|
||||||
|
if(batteries.any{ it.name.equals(value, ignoreCase = true) }){
|
||||||
|
model.batteryHasError = true
|
||||||
|
model.batteryHelperId = R.string.helper_battery_not_unique
|
||||||
|
}
|
||||||
|
},
|
||||||
|
labelId = R.string.hint_enter_battery_name,
|
||||||
|
leadingIcon = { Icon(Icons.Default.Tag, "Icon Tag") },
|
||||||
|
isError = model.batteryHasError,
|
||||||
|
helperTextId = model.batteryHelperId
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(Modifier.size(innerPadding))
|
||||||
|
|
||||||
|
MyOutlinedTextFieldWithSuffix(
|
||||||
|
value = model.declaredCapacity,
|
||||||
|
onValueChange = {
|
||||||
|
model.declaredCapacity = validateDecimal(it, model.declaredCapacity)
|
||||||
|
},
|
||||||
|
labelId = R.string.hint_enter_declared_capacity,
|
||||||
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
|
||||||
|
leadingIcon = { Icon(Icons.Default.BatteryFull, "Icon Battery Full") },
|
||||||
|
suffix = "Ah"
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(Modifier.size(outerPadding))
|
||||||
|
|
||||||
|
ExtendedFloatingActionButton(
|
||||||
|
onClick = {
|
||||||
|
if(model.batteryName.isBlank()) model.batteryHasError = true
|
||||||
|
|
||||||
|
if(!model.batteryHasError && model.saveBattery(model.batteryName, model.declaredCapacity)){
|
||||||
|
navController.navigate("home")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon = { Icon(Icons.Default.Save, "Icon Save") },
|
||||||
|
text = { Text(stringResource(R.string.button_save_battery)) },
|
||||||
|
modifier = Modifier.align(Alignment.End)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
package com.sockenklaus.batterytracker.ui.composables
|
||||||
|
|
||||||
|
import android.app.DatePickerDialog
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material.*
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.BatteryChargingFull
|
||||||
|
import androidx.compose.material.icons.filled.EditCalendar
|
||||||
|
import androidx.compose.material.icons.filled.Save
|
||||||
|
import androidx.compose.material.icons.filled.Tag
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.TextRange
|
||||||
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.sockenklaus.batterytracker.R
|
||||||
|
import com.sockenklaus.batterytracker.ui.composables.util.MyOutlinedTextFieldWithSuffix
|
||||||
|
import com.sockenklaus.batterytracker.ui.models.AddChargeViewModel
|
||||||
|
import com.sockenklaus.batterytracker.ui.theme.Gray500
|
||||||
|
import com.sockenklaus.batterytracker.util.validateDecimal
|
||||||
|
import java.time.*
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
import java.time.format.FormatStyle
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
|
@Composable
|
||||||
|
fun AddCharge(navController: NavController){
|
||||||
|
val outerPadding = 24.dp
|
||||||
|
val innerPadding = 16.dp
|
||||||
|
|
||||||
|
var bIdExpanded by remember { mutableStateOf(false)}
|
||||||
|
val model: AddChargeViewModel = viewModel()
|
||||||
|
val batteries by model.batteries.observeAsState(emptyList())
|
||||||
|
|
||||||
|
Column(
|
||||||
|
Modifier.padding(outerPadding)
|
||||||
|
) {
|
||||||
|
ExposedDropdownMenuBox(
|
||||||
|
expanded = bIdExpanded,
|
||||||
|
onExpandedChange = { bIdExpanded = !bIdExpanded}
|
||||||
|
) {
|
||||||
|
|
||||||
|
val filteringOptions = batteries.filter { it.name.contains(model.batteryId.text, ignoreCase = true)}
|
||||||
|
OutlinedTextField(
|
||||||
|
value = model.batteryId,
|
||||||
|
onValueChange = {
|
||||||
|
model.batteryId = it
|
||||||
|
model.batteryHasError = false
|
||||||
|
model.batteryHelper = R.string.helper_required
|
||||||
|
bIdExpanded = filteringOptions.size > 1
|
||||||
|
},
|
||||||
|
label = { Text(stringResource(R.string.select_battery_id)) },
|
||||||
|
leadingIcon = { Icon(Icons.Default.Tag, null) },
|
||||||
|
trailingIcon = {
|
||||||
|
ExposedDropdownMenuDefaults.TrailingIcon(expanded = bIdExpanded)
|
||||||
|
},
|
||||||
|
colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(),
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
isError = model.batteryHasError
|
||||||
|
)
|
||||||
|
|
||||||
|
if(filteringOptions.isNotEmpty()) {
|
||||||
|
ExposedDropdownMenu(
|
||||||
|
expanded = bIdExpanded,
|
||||||
|
onDismissRequest = { bIdExpanded = false }
|
||||||
|
) {
|
||||||
|
for (filteringOption in filteringOptions) {
|
||||||
|
DropdownMenuItem(
|
||||||
|
onClick = {
|
||||||
|
model.batteryId = TextFieldValue(
|
||||||
|
text = filteringOption.name,
|
||||||
|
selection = TextRange(filteringOption.name.length)
|
||||||
|
)
|
||||||
|
bIdExpanded = false
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(filteringOption.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = stringResource(model.batteryHelper),
|
||||||
|
style = MaterialTheme.typography.caption,
|
||||||
|
color = if(model.batteryHasError) MaterialTheme.colors.error else Gray500,
|
||||||
|
modifier = Modifier.padding(start = 16.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(Modifier.size(innerPadding))
|
||||||
|
|
||||||
|
MyOutlinedTextFieldWithSuffix(
|
||||||
|
value = model.charge,
|
||||||
|
onValueChange = {
|
||||||
|
model.charge = validateDecimal(it, model.charge)
|
||||||
|
model.chargeHasError = false
|
||||||
|
},
|
||||||
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
|
||||||
|
labelId = R.string.hint_charge,
|
||||||
|
leadingIcon = { Icon(Icons.Default.BatteryChargingFull, "Icon Battery Charging Full") },
|
||||||
|
isError = model.chargeHasError,
|
||||||
|
helperTextId = R.string.helper_required,
|
||||||
|
suffix = "Ah"
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(Modifier.size(innerPadding))
|
||||||
|
|
||||||
|
ChargeDatePicker(
|
||||||
|
date = model.date,
|
||||||
|
onSelect = { model.date = it}
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(Modifier.size(outerPadding))
|
||||||
|
|
||||||
|
ExtendedFloatingActionButton(
|
||||||
|
text = { Text(stringResource(R.string.button_save_charge)) },
|
||||||
|
onClick = {
|
||||||
|
if(!batteries.any{ it.name == model.batteryId.text }){
|
||||||
|
model.batteryHasError = true
|
||||||
|
model.batteryHelper = R.string.helper_battery_not_found
|
||||||
|
}
|
||||||
|
if(model.batteryId.text.isBlank()){
|
||||||
|
model.batteryHasError = true
|
||||||
|
model.batteryHelper = R.string.helper_required
|
||||||
|
}
|
||||||
|
if(model.charge.isBlank()){
|
||||||
|
model.chargeHasError = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!model.batteryHasError && !model.chargeHasError && model.saveCharge(batteries, model.batteryId.text, model.charge, model.date)){
|
||||||
|
navController.navigate("home")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon = { Icon(Icons.Default.Save, "Icon Save") },
|
||||||
|
modifier = Modifier.align(Alignment.End)
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ChargeDatePicker(
|
||||||
|
date: LocalDate,
|
||||||
|
onSelect: (LocalDate) -> Unit,
|
||||||
|
){
|
||||||
|
val outputFormat = DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).withLocale(Locale.GERMANY)
|
||||||
|
val datePickerDialog = DatePickerDialog(
|
||||||
|
LocalContext.current
|
||||||
|
)
|
||||||
|
datePickerDialog.setOnDateSetListener { _, year, month, day ->
|
||||||
|
onSelect(LocalDate.of(year, month + 1, day))
|
||||||
|
}
|
||||||
|
datePickerDialog.updateDate(date.year, date.monthValue - 1, date.dayOfMonth)
|
||||||
|
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
datePickerDialog.show()
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
){
|
||||||
|
Icon(
|
||||||
|
Icons.Default.EditCalendar,
|
||||||
|
"Edit Calendar Icon",
|
||||||
|
Modifier.size(ButtonDefaults.IconSize),
|
||||||
|
)
|
||||||
|
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
|
||||||
|
Text("Date: ${date.format(outputFormat)}")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package com.sockenklaus.batterytracker.ui.composables
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.sockenklaus.batterytracker.R
|
||||||
|
import com.sockenklaus.batterytracker.room.entities.Battery
|
||||||
|
import com.sockenklaus.batterytracker.ui.composables.util.MyOutlinedTextFieldWithSuffix
|
||||||
|
import com.sockenklaus.batterytracker.ui.models.HomeViewModel
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
|
@Composable
|
||||||
|
fun Home(navController: NavController) {
|
||||||
|
val model: HomeViewModel = viewModel()
|
||||||
|
val batteries by model.batteries.observeAsState(emptyList<Battery>())
|
||||||
|
var filterText by remember { mutableStateOf("")}
|
||||||
|
|
||||||
|
val filteredList = batteries.filter { it.name.contains(filterText, ignoreCase = true) }
|
||||||
|
val modHorizontalPadding = Modifier.padding(horizontal = 16.dp)
|
||||||
|
|
||||||
|
Column {
|
||||||
|
MyOutlinedTextFieldWithSuffix(
|
||||||
|
value = filterText,
|
||||||
|
onValueChange = { filterText = it },
|
||||||
|
labelId = R.string.hint_filter_batteries,
|
||||||
|
modifier = Modifier.padding(
|
||||||
|
start = 16.dp,
|
||||||
|
end = 16.dp,
|
||||||
|
top = 16.dp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
LazyColumn(
|
||||||
|
state = LazyListState()
|
||||||
|
) {
|
||||||
|
items(filteredList){ battery ->
|
||||||
|
ListItem(
|
||||||
|
text = { Text(battery.name) },
|
||||||
|
secondaryText = {
|
||||||
|
if(battery.declaredCapacity != null){
|
||||||
|
Text("Capacity: ${battery.declaredCapacity} Ah")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Divider(modHorizontalPadding)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
class HomeFragment : Fragment() {
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
|
||||||
|
return ComposeView(requireContext()).apply {
|
||||||
|
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||||
|
|
||||||
|
setContent {
|
||||||
|
BatteryTrackerTheme() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
}*/
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.sockenklaus.batterytracker.ui.composables
|
package com.sockenklaus.batterytracker.ui.composables.util
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
@@ -9,18 +9,104 @@ import androidx.compose.foundation.text.KeyboardOptions
|
|||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
import androidx.compose.material.TextFieldDefaults.OutlinedTextFieldDecorationBox
|
import androidx.compose.material.TextFieldDefaults.OutlinedTextFieldDecorationBox
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Shape
|
import androidx.compose.ui.graphics.*
|
||||||
import androidx.compose.ui.graphics.SolidColor
|
|
||||||
import androidx.compose.ui.graphics.takeOrElse
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.input.VisualTransformation
|
import androidx.compose.ui.text.input.VisualTransformation
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.sockenklaus.batterytracker.ui.theme.Gray500
|
import com.sockenklaus.batterytracker.ui.theme.Gray500
|
||||||
|
|
||||||
|
/*@Composable
|
||||||
|
fun TopAppBar(
|
||||||
|
title: @Composable () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
navigationIcon: @Composable (() -> Unit)? = null,
|
||||||
|
actions: @Composable RowScope.() -> Unit = {},
|
||||||
|
backgroundColor: Color = MaterialTheme.colors.primarySurface,
|
||||||
|
contentColor: Color = contentColorFor(backgroundColor),
|
||||||
|
elevation: Dp = AppBarDefaults.TopAppBarElevation
|
||||||
|
) {
|
||||||
|
AppBar(
|
||||||
|
backgroundColor,
|
||||||
|
contentColor,
|
||||||
|
elevation,
|
||||||
|
AppBarDefaults.ContentPadding,
|
||||||
|
RectangleShape,
|
||||||
|
modifier
|
||||||
|
) {
|
||||||
|
if (navigationIcon == null) {
|
||||||
|
Spacer(Modifier.width(16.dp - 4.dp))
|
||||||
|
} else {
|
||||||
|
Row(Modifier.fillMaxHeight().width(72.dp - 4.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalContentAlpha provides ContentAlpha.high,
|
||||||
|
content = navigationIcon
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
Modifier.fillMaxHeight().weight(1f),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
ProvideTextStyle(value = MaterialTheme.typography.h6) {
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalContentAlpha provides ContentAlpha.high,
|
||||||
|
content = title
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
|
||||||
|
Row(
|
||||||
|
Modifier.fillMaxHeight(),
|
||||||
|
horizontalArrangement = Arrangement.End,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
content = actions
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
private fun AppBar(
|
||||||
|
backgroundColor: Color,
|
||||||
|
contentColor: Color,
|
||||||
|
elevation: Dp,
|
||||||
|
contentPadding: PaddingValues,
|
||||||
|
shape: Shape,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
content: @Composable RowScope.() -> Unit
|
||||||
|
) {
|
||||||
|
Surface(
|
||||||
|
color = backgroundColor,
|
||||||
|
contentColor = contentColor,
|
||||||
|
elevation = elevation,
|
||||||
|
shape = shape,
|
||||||
|
modifier = modifier
|
||||||
|
) {
|
||||||
|
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
|
||||||
|
Row(
|
||||||
|
Modifier.fillMaxWidth()
|
||||||
|
.padding(contentPadding)
|
||||||
|
.height(56.dp),
|
||||||
|
horizontalArrangement = Arrangement.Start,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
content = content
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
fun MyOutlinedTextFieldWithSuffix(
|
fun MyOutlinedTextFieldWithSuffix(
|
||||||
value: String,
|
value: String,
|
||||||
onValueChange: (String) -> Unit,
|
onValueChange: (String) -> Unit,
|
||||||
@@ -56,10 +142,10 @@ fun MyOutlinedTextFieldWithSuffix(
|
|||||||
Gray500
|
Gray500
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
|
||||||
BasicTextField(
|
BasicTextField(
|
||||||
value = value,
|
value = value,
|
||||||
modifier = modifier.padding(top = 8.dp)
|
modifier = modifier
|
||||||
|
.padding(top = 8.dp)
|
||||||
|
|
||||||
.background(colors.backgroundColor(enabled).value, shape)
|
.background(colors.backgroundColor(enabled).value, shape)
|
||||||
.defaultMinSize(
|
.defaultMinSize(
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
package com.sockenklaus.batterytracker.ui.fragments.add_battery
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.compose.foundation.layout.*
|
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
|
||||||
import androidx.compose.material.*
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.*
|
|
||||||
import androidx.compose.runtime.*
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.platform.ComposeView
|
|
||||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.navigation.fragment.findNavController
|
|
||||||
import com.sockenklaus.batterytracker.R
|
|
||||||
import com.sockenklaus.batterytracker.room.entities.Battery
|
|
||||||
import com.sockenklaus.batterytracker.ui.composables.MyOutlinedTextFieldWithSuffix
|
|
||||||
import com.sockenklaus.batterytracker.ui.theme.BatteryTrackerTheme
|
|
||||||
import com.sockenklaus.batterytracker.util.validateDecimal
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A simple [Fragment] subclass.
|
|
||||||
* Use the [AddBatteryFragment.newInstance] factory method to
|
|
||||||
* create an instance of this fragment.
|
|
||||||
*/
|
|
||||||
class AddBatteryFragment : Fragment() {
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View {
|
|
||||||
|
|
||||||
val model = ViewModelProvider(this)[AddBatteryViewModel::class.java]
|
|
||||||
|
|
||||||
var batteries by mutableStateOf(emptyList<Battery>())
|
|
||||||
|
|
||||||
model.batteries.observe(viewLifecycleOwner) {
|
|
||||||
batteries = it
|
|
||||||
}
|
|
||||||
|
|
||||||
return ComposeView(requireContext()).apply {
|
|
||||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
|
||||||
|
|
||||||
setContent {
|
|
||||||
BatteryTrackerTheme() {
|
|
||||||
val outerPadding = 24.dp
|
|
||||||
val innerPadding = 16.dp
|
|
||||||
|
|
||||||
Column(
|
|
||||||
Modifier.padding(outerPadding)
|
|
||||||
) {
|
|
||||||
|
|
||||||
MyOutlinedTextFieldWithSuffix(
|
|
||||||
value = model.batteryName,
|
|
||||||
onValueChange = { value ->
|
|
||||||
model.batteryName = value
|
|
||||||
model.batteryHasError = false
|
|
||||||
model.batteryHelperId = R.string.helper_required
|
|
||||||
if(batteries.any{ it.name.equals(value, ignoreCase = true) }){
|
|
||||||
model.batteryHasError = true
|
|
||||||
model.batteryHelperId = R.string.helper_battery_not_unique
|
|
||||||
}
|
|
||||||
},
|
|
||||||
labelId = R.string.hint_enter_battery_name,
|
|
||||||
leadingIcon = { Icon(Icons.Default.Tag, "Icon Tag") },
|
|
||||||
isError = model.batteryHasError,
|
|
||||||
helperTextId = model.batteryHelperId
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(Modifier.size(innerPadding))
|
|
||||||
|
|
||||||
MyOutlinedTextFieldWithSuffix(
|
|
||||||
value = model.declaredCapacity,
|
|
||||||
onValueChange = {
|
|
||||||
model.declaredCapacity = validateDecimal(it, model.declaredCapacity)
|
|
||||||
},
|
|
||||||
labelId = R.string.hint_enter_declared_capacity,
|
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
|
|
||||||
leadingIcon = { Icon(Icons.Default.BatteryFull, "Icon Battery Full") },
|
|
||||||
suffix = "Ah"
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(Modifier.size(outerPadding))
|
|
||||||
|
|
||||||
ExtendedFloatingActionButton(
|
|
||||||
onClick = {
|
|
||||||
if(model.batteryName.isBlank()) model.batteryHasError = true
|
|
||||||
|
|
||||||
if(!model.batteryHasError && model.saveBattery(model.batteryName, model.declaredCapacity)){
|
|
||||||
findNavController().navigate(R.id.action_nav_add_battery_to_nav_home)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
icon = { Icon(Icons.Default.Save, "Icon Save") },
|
|
||||||
text = { Text(stringResource(R.string.button_save_battery)) },
|
|
||||||
modifier = Modifier.align(Alignment.End)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*companion object {
|
|
||||||
|
|
||||||
* Use this factory method to create a new instance of
|
|
||||||
* this fragment using the provided parameters.
|
|
||||||
*
|
|
||||||
* @param param1 Parameter 1.
|
|
||||||
* @param param2 Parameter 2.
|
|
||||||
* @return A new instance of fragment AddBatteryFragment.
|
|
||||||
*//*
|
|
||||||
// TODO: Rename and change types and number of parameters
|
|
||||||
@JvmStatic
|
|
||||||
fun newInstance(param1: String, param2: String) =
|
|
||||||
AddBatteryFragment().apply {
|
|
||||||
arguments = Bundle().apply {
|
|
||||||
putString(ARG_PARAM1, param1)
|
|
||||||
putString(ARG_PARAM2, param2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
@@ -1,212 +0,0 @@
|
|||||||
package com.sockenklaus.batterytracker.ui.fragments.add_charge
|
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.compose.foundation.layout.*
|
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
|
||||||
import androidx.compose.material.*
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.BatteryChargingFull
|
|
||||||
import androidx.compose.material.icons.filled.EditCalendar
|
|
||||||
import androidx.compose.material.icons.filled.Save
|
|
||||||
import androidx.compose.material.icons.filled.Tag
|
|
||||||
import androidx.compose.runtime.*
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.platform.ComposeView
|
|
||||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.TextRange
|
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.navigation.fragment.findNavController
|
|
||||||
import com.google.android.material.datepicker.MaterialDatePicker
|
|
||||||
import com.sockenklaus.batterytracker.R
|
|
||||||
import com.sockenklaus.batterytracker.room.entities.Battery
|
|
||||||
import com.sockenklaus.batterytracker.ui.composables.MyOutlinedTextFieldWithSuffix
|
|
||||||
import com.sockenklaus.batterytracker.ui.theme.BatteryTrackerTheme
|
|
||||||
import com.sockenklaus.batterytracker.ui.theme.Gray500
|
|
||||||
import com.sockenklaus.batterytracker.util.validateDecimal
|
|
||||||
import java.time.*
|
|
||||||
import java.time.format.DateTimeFormatter
|
|
||||||
import java.time.format.FormatStyle
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class AddChargeFragment : Fragment() {
|
|
||||||
|
|
||||||
private lateinit var model: AddChargeViewModel
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater,
|
|
||||||
container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View {
|
|
||||||
|
|
||||||
model = ViewModelProvider(this)[AddChargeViewModel::class.java]
|
|
||||||
var batteries by mutableStateOf(emptyList<Battery>())
|
|
||||||
|
|
||||||
model.batteries.observe(this.viewLifecycleOwner){
|
|
||||||
batteries = it
|
|
||||||
}
|
|
||||||
|
|
||||||
return ComposeView(requireContext()).apply{
|
|
||||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
|
||||||
|
|
||||||
setContent {
|
|
||||||
BatteryTrackerTheme() {
|
|
||||||
val outerPadding = 24.dp
|
|
||||||
val innerPadding = 16.dp
|
|
||||||
|
|
||||||
var bIdExpanded by remember { mutableStateOf(false)}
|
|
||||||
|
|
||||||
Column(
|
|
||||||
Modifier.padding(outerPadding)
|
|
||||||
) {
|
|
||||||
ExposedDropdownMenuBox(
|
|
||||||
expanded = bIdExpanded,
|
|
||||||
onExpandedChange = { bIdExpanded = !bIdExpanded}
|
|
||||||
) {
|
|
||||||
|
|
||||||
val filteringOptions = batteries.filter { it.name.contains(model.batteryId.text, ignoreCase = true)}
|
|
||||||
OutlinedTextField(
|
|
||||||
value = model.batteryId,
|
|
||||||
onValueChange = {
|
|
||||||
model.batteryId = it
|
|
||||||
model.batteryHasError = false
|
|
||||||
model.batteryHelper = R.string.helper_required
|
|
||||||
bIdExpanded = filteringOptions.size > 1
|
|
||||||
},
|
|
||||||
label = { Text(stringResource(R.string.select_battery_id)) },
|
|
||||||
leadingIcon = { Icon(Icons.Default.Tag, null) },
|
|
||||||
trailingIcon = {
|
|
||||||
ExposedDropdownMenuDefaults.TrailingIcon(expanded = bIdExpanded)
|
|
||||||
},
|
|
||||||
colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(),
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
isError = model.batteryHasError
|
|
||||||
)
|
|
||||||
|
|
||||||
if(filteringOptions.isNotEmpty()) {
|
|
||||||
ExposedDropdownMenu(
|
|
||||||
expanded = bIdExpanded,
|
|
||||||
onDismissRequest = { bIdExpanded = false }
|
|
||||||
) {
|
|
||||||
for (filteringOption in filteringOptions) {
|
|
||||||
DropdownMenuItem(
|
|
||||||
onClick = {
|
|
||||||
model.batteryId = TextFieldValue(
|
|
||||||
text = filteringOption.name,
|
|
||||||
selection = TextRange(filteringOption.name.length)
|
|
||||||
)
|
|
||||||
bIdExpanded = false
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Text(filteringOption.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Text(
|
|
||||||
text = stringResource(model.batteryHelper),
|
|
||||||
style = MaterialTheme.typography.caption,
|
|
||||||
color = if(model.batteryHasError) MaterialTheme.colors.error else Gray500,
|
|
||||||
modifier = Modifier.padding(start = 16.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(Modifier.size(innerPadding))
|
|
||||||
|
|
||||||
MyOutlinedTextFieldWithSuffix(
|
|
||||||
value = model.charge,
|
|
||||||
onValueChange = {
|
|
||||||
model.charge = validateDecimal(it, model.charge)
|
|
||||||
model.chargeHasError = false
|
|
||||||
},
|
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
|
|
||||||
labelId = R.string.hint_charge,
|
|
||||||
leadingIcon = { Icon(Icons.Default.BatteryChargingFull, "Icon Battery Charging Full") },
|
|
||||||
isError = model.chargeHasError,
|
|
||||||
helperTextId = R.string.helper_required,
|
|
||||||
suffix = "Ah"
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(Modifier.size(innerPadding))
|
|
||||||
|
|
||||||
ChargeDatePicker(
|
|
||||||
date = model.date,
|
|
||||||
onSelect = { model.date = it}
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(Modifier.size(outerPadding))
|
|
||||||
|
|
||||||
ExtendedFloatingActionButton(
|
|
||||||
text = { Text(stringResource(R.string.button_save_charge)) },
|
|
||||||
onClick = {
|
|
||||||
if(!batteries.any{ it.name == model.batteryId.text }){
|
|
||||||
model.batteryHasError = true
|
|
||||||
model.batteryHelper = R.string.helper_battery_not_found
|
|
||||||
}
|
|
||||||
if(model.batteryId.text.isBlank()){
|
|
||||||
model.batteryHasError = true
|
|
||||||
model.batteryHelper = R.string.helper_required
|
|
||||||
}
|
|
||||||
if(model.charge.isBlank()){
|
|
||||||
model.chargeHasError = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!model.batteryHasError && !model.chargeHasError && model.saveCharge(batteries, model.batteryId.text, model.charge, model.date)){
|
|
||||||
findNavController().navigate(R.id.action_nav_add_charge_to_nav_home)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
icon = { Icon(Icons.Default.Save, "Icon Save") },
|
|
||||||
modifier = Modifier.align(Alignment.End)
|
|
||||||
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun ChargeDatePicker(
|
|
||||||
date: LocalDateTime,
|
|
||||||
onSelect: (LocalDateTime) -> Unit,
|
|
||||||
) {
|
|
||||||
val dateFormat: DateTimeFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).withLocale(Locale.GERMANY)
|
|
||||||
|
|
||||||
val datePicker = MaterialDatePicker.Builder.datePicker()
|
|
||||||
.setTitleText("Select date")
|
|
||||||
.setSelection( date.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() )
|
|
||||||
.build()
|
|
||||||
|
|
||||||
datePicker.addOnPositiveButtonClickListener {
|
|
||||||
onSelect(LocalDateTime.ofInstant(Instant.ofEpochMilli(it), ZoneId.systemDefault()))
|
|
||||||
}
|
|
||||||
|
|
||||||
TextButton(
|
|
||||||
onClick = {
|
|
||||||
datePicker.show(parentFragmentManager, "tag")
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
){
|
|
||||||
Icon(
|
|
||||||
Icons.Default.EditCalendar,
|
|
||||||
"Edit Calendar Icon",
|
|
||||||
Modifier.size(ButtonDefaults.IconSize),
|
|
||||||
)
|
|
||||||
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
|
|
||||||
Text("Date: ${date.format(dateFormat)}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
|
||||||
super.onDestroyView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
package com.sockenklaus.batterytracker.ui.fragments.home
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
|
||||||
import androidx.compose.foundation.lazy.items
|
|
||||||
import androidx.compose.material.*
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.platform.ComposeView
|
|
||||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import com.sockenklaus.batterytracker.R
|
|
||||||
import com.sockenklaus.batterytracker.room.entities.Battery
|
|
||||||
import com.sockenklaus.batterytracker.ui.composables.MyOutlinedTextFieldWithSuffix
|
|
||||||
import com.sockenklaus.batterytracker.ui.theme.BatteryTrackerTheme
|
|
||||||
|
|
||||||
class HomeFragment : Fragment() {
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater,
|
|
||||||
container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View {
|
|
||||||
|
|
||||||
return ComposeView(requireContext()).apply {
|
|
||||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
|
||||||
|
|
||||||
setContent {
|
|
||||||
BatteryTrackerTheme() {
|
|
||||||
val model: HomeViewModel = viewModel()
|
|
||||||
var batteries by remember { mutableStateOf(emptyList<Battery>()) }
|
|
||||||
var filterText by remember { mutableStateOf("")}
|
|
||||||
|
|
||||||
model.batteries.observe(viewLifecycleOwner) {
|
|
||||||
batteries = it
|
|
||||||
}
|
|
||||||
|
|
||||||
val filteredList = batteries.filter { it.name.contains(filterText, ignoreCase = true) }
|
|
||||||
val modHorizontalPadding = Modifier.padding(horizontal = 16.dp)
|
|
||||||
|
|
||||||
Column {
|
|
||||||
MyOutlinedTextFieldWithSuffix(
|
|
||||||
value = filterText,
|
|
||||||
onValueChange = { filterText = it },
|
|
||||||
labelId = R.string.hint_filter_batteries,
|
|
||||||
modifier = Modifier.padding(
|
|
||||||
start = 16.dp,
|
|
||||||
end = 16.dp,
|
|
||||||
top = 16.dp
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
LazyColumn(
|
|
||||||
state = LazyListState()
|
|
||||||
) {
|
|
||||||
items(filteredList){ battery ->
|
|
||||||
ListItem(
|
|
||||||
text = { Text(battery.name) },
|
|
||||||
secondaryText = {
|
|
||||||
if(battery.declaredCapacity != null){
|
|
||||||
Text("Capacity: ${battery.declaredCapacity} Ah")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
Divider(modHorizontalPadding)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
|
||||||
super.onDestroyView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package com.sockenklaus.batterytracker.ui.fragments.settings
|
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import com.sockenklaus.batterytracker.R
|
|
||||||
|
|
||||||
class SettingsFragment : Fragment() {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun newInstance() = SettingsFragment()
|
|
||||||
}
|
|
||||||
|
|
||||||
private lateinit var viewModel: SettingsViewModel
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View? {
|
|
||||||
viewModel = ViewModelProvider(this)[SettingsViewModel::class.java]
|
|
||||||
|
|
||||||
return inflater.inflate(R.layout.fragment_settings, container, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package com.sockenklaus.batterytracker.ui.fragments.settings
|
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
|
|
||||||
class SettingsViewModel : ViewModel() {
|
|
||||||
// TODO: Implement the ViewModel
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.sockenklaus.batterytracker.ui.fragments.add_battery
|
package com.sockenklaus.batterytracker.ui.models
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -16,7 +16,6 @@ import java.lang.Exception
|
|||||||
|
|
||||||
class AddBatteryViewModel(application: Application): AndroidViewModel(application) {
|
class AddBatteryViewModel(application: Application): AndroidViewModel(application) {
|
||||||
|
|
||||||
private val app = application
|
|
||||||
private val db = BatteryTrackerDB.getInstance(application)
|
private val db = BatteryTrackerDB.getInstance(application)
|
||||||
val batteries = db.batteryDao().getBatteries().asLiveData()
|
val batteries = db.batteryDao().getBatteries().asLiveData()
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.sockenklaus.batterytracker.ui.fragments.add_charge
|
package com.sockenklaus.batterytracker.ui.models
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -15,7 +15,7 @@ import com.sockenklaus.batterytracker.room.entities.Battery
|
|||||||
import com.sockenklaus.batterytracker.room.entities.Charge
|
import com.sockenklaus.batterytracker.room.entities.Charge
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDate
|
||||||
|
|
||||||
class AddChargeViewModel(application: Application) : AndroidViewModel(application) {
|
class AddChargeViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ class AddChargeViewModel(application: Application) : AndroidViewModel(applicatio
|
|||||||
var batteries: LiveData<List<Battery>> = db.batteryDao().getBatteries().asLiveData()
|
var batteries: LiveData<List<Battery>> = db.batteryDao().getBatteries().asLiveData()
|
||||||
|
|
||||||
var batteryId by mutableStateOf(TextFieldValue(""))
|
var batteryId by mutableStateOf(TextFieldValue(""))
|
||||||
var date: LocalDateTime by mutableStateOf(LocalDateTime.now())
|
var date: LocalDate by mutableStateOf(LocalDate.now())
|
||||||
var charge by mutableStateOf("")
|
var charge by mutableStateOf("")
|
||||||
|
|
||||||
var batteryHasError by mutableStateOf(false)
|
var batteryHasError by mutableStateOf(false)
|
||||||
@@ -35,7 +35,7 @@ class AddChargeViewModel(application: Application) : AndroidViewModel(applicatio
|
|||||||
batteryList: List<Battery>,
|
batteryList: List<Battery>,
|
||||||
batteryName: String,
|
batteryName: String,
|
||||||
charge: String,
|
charge: String,
|
||||||
date: LocalDateTime
|
date: LocalDate
|
||||||
):Boolean {
|
):Boolean {
|
||||||
|
|
||||||
val battery = batteryList.find { it.name == batteryName }
|
val battery = batteryList.find { it.name == batteryName }
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.sockenklaus.batterytracker.ui.fragments.home
|
package com.sockenklaus.batterytracker.ui.models
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.*
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.sockenklaus.batterytracker.ui.models
|
||||||
|
|
||||||
|
import androidx.compose.material.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
|
||||||
|
class MainViewModel : ViewModel() {
|
||||||
|
|
||||||
|
var showAppBar by mutableStateOf(true)
|
||||||
|
var appTitle by mutableStateOf("Home")
|
||||||
|
lateinit var navController: NavHostController
|
||||||
|
lateinit var scaffoldState: ScaffoldState
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun init(){
|
||||||
|
navController = rememberNavController()
|
||||||
|
scaffoldState = rememberScaffoldState()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.drawerlayout.widget.DrawerLayout 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:id="@+id/drawer_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:fitsSystemWindows="true"
|
|
||||||
tools:openDrawer="start">
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/app_bar_main"
|
|
||||||
layout="@layout/app_bar_main"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" />
|
|
||||||
|
|
||||||
<com.google.android.material.navigation.NavigationView
|
|
||||||
android:id="@+id/nav_view"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_gravity="start"
|
|
||||||
android:fitsSystemWindows="true"
|
|
||||||
app:headerLayout="@layout/nav_header_main"
|
|
||||||
app:menu="@menu/activity_main_drawer" />
|
|
||||||
|
|
||||||
</androidx.drawerlayout.widget.DrawerLayout>
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout 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"
|
|
||||||
tools:context=".MainActivity">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:theme="@style/Theme.BatteryTracker.AppBarOverlay">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?attr/actionBarSize"
|
|
||||||
android:background="?attr/colorPrimary"
|
|
||||||
app:popupTheme="@style/Theme.BatteryTracker.PopupOverlay" />
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/content_main"
|
|
||||||
layout="@layout/content_main" />
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.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"
|
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
|
||||||
tools:showIn="@layout/app_bar_main">
|
|
||||||
|
|
||||||
<fragment
|
|
||||||
android:id="@+id/nav_host_fragment_content_main"
|
|
||||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:defaultNavHost="true"
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:navGraph="@navigation/mobile_navigation" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.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"
|
|
||||||
tools:context=".ui.fragments.settings.SettingsFragment">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:text="Hello"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
|
||||||
android:id="@+id/fab"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="bottom|end"
|
|
||||||
android:layout_marginEnd="32dp"
|
|
||||||
android:layout_marginBottom="32dp"
|
|
||||||
android:contentDescription="@string/button_save_settings"
|
|
||||||
android:text="@string/button_save_settings"
|
|
||||||
app:icon="@drawable/ic_baseline_check_24"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<TextView
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="16dp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:textAppearance="?attr/textAppearanceSubtitle1">
|
|
||||||
|
|
||||||
</TextView>
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout 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="@dimen/nav_header_height"
|
|
||||||
android:background="@drawable/side_nav_bar"
|
|
||||||
android:gravity="bottom"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
|
||||||
android:paddingTop="@dimen/activity_vertical_margin"
|
|
||||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
|
||||||
android:paddingBottom="@dimen/activity_vertical_margin">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingTop="@dimen/nav_header_vertical_spacing"
|
|
||||||
android:text="@string/nav_header_title"
|
|
||||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
|
|
||||||
/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
tools:showIn="navigation_view">
|
|
||||||
|
|
||||||
<group android:checkableBehavior="single">
|
|
||||||
<item
|
|
||||||
android:id="@+id/nav_home"
|
|
||||||
android:icon="@drawable/ic_baseline_home_24"
|
|
||||||
android:title="@string/menu_home" />
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/nav_add_charge"
|
|
||||||
android:title="@string/menu_add_charge"
|
|
||||||
android:icon="@drawable/ic_baseline_battery_charging_full_24"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/nav_add_battery"
|
|
||||||
android:title="@string/menu_add_battery"
|
|
||||||
android:icon="@drawable/ic_baseline_battery_5_bar_24"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/nav_settings"
|
|
||||||
android:icon="@drawable/ic_baseline_settings_24"
|
|
||||||
android:title="@string/menu_settings" />
|
|
||||||
</group>
|
|
||||||
</menu>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
<!-- <item
|
|
||||||
android:id="@+id/action_settings"
|
|
||||||
android:orderInCategory="100"
|
|
||||||
android:title="@string/action_settings"
|
|
||||||
app:showAsAction="never" />-->
|
|
||||||
</menu>
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<navigation 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:id="@+id/mobile_navigation"
|
|
||||||
app:startDestination="@+id/nav_home">
|
|
||||||
|
|
||||||
<fragment
|
|
||||||
android:id="@+id/nav_home"
|
|
||||||
android:name="com.sockenklaus.batterytracker.ui.fragments.home.HomeFragment"
|
|
||||||
android:label="@string/menu_home" />
|
|
||||||
|
|
||||||
<fragment
|
|
||||||
android:id="@+id/nav_add_charge"
|
|
||||||
android:name="com.sockenklaus.batterytracker.ui.fragments.add_charge.AddChargeFragment"
|
|
||||||
android:label="@string/menu_add_charge">
|
|
||||||
<action
|
|
||||||
android:id="@+id/action_nav_add_charge_to_nav_home"
|
|
||||||
app:destination="@id/nav_home" />
|
|
||||||
</fragment>
|
|
||||||
|
|
||||||
<fragment
|
|
||||||
android:id="@+id/nav_add_battery"
|
|
||||||
android:name="com.sockenklaus.batterytracker.ui.fragments.add_battery.AddBatteryFragment"
|
|
||||||
android:label="@string/menu_add_battery" >
|
|
||||||
<action
|
|
||||||
android:id="@+id/action_nav_add_battery_to_nav_home"
|
|
||||||
app:destination="@id/nav_home" />
|
|
||||||
</fragment>
|
|
||||||
|
|
||||||
<fragment
|
|
||||||
android:id="@+id/nav_settings"
|
|
||||||
android:name="com.sockenklaus.batterytracker.ui.fragments.settings.SettingsFragment"
|
|
||||||
android:label="@string/action_settings"
|
|
||||||
tools:layout="@layout/fragment_settings" />
|
|
||||||
</navigation>
|
|
||||||
@@ -1,21 +1,19 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">BatteryTracker</string>
|
<string name="app_name">BatteryTracker</string>
|
||||||
<string name="navigation_drawer_open">Open navigation drawer</string>
|
|
||||||
<string name="navigation_drawer_close">Close navigation drawer</string>
|
|
||||||
<string name="nav_header_title">Navigation</string>
|
|
||||||
<string name="nav_header_subtitle">android.studio@android.com</string>
|
<string name="nav_header_subtitle">android.studio@android.com</string>
|
||||||
<string name="nav_header_desc">Navigation header</string>
|
<string name="nav_header_desc">Navigation header</string>
|
||||||
<string name="action_settings">Settings</string>
|
<string name="action_settings">Settings</string>
|
||||||
|
|
||||||
<string name="menu_home">Home</string>
|
|
||||||
<string name="menu_gallery">Gallery</string>
|
|
||||||
<string name="menu_slideshow">Slideshow</string>
|
<string name="menu_slideshow">Slideshow</string>
|
||||||
<!-- TODO: Remove or change this placeholder text -->
|
<!-- TODO: Remove or change this placeholder text -->
|
||||||
<string name="hello_blank_fragment">Hello blank fragment</string>
|
<string name="hello_blank_fragment">Hello blank fragment</string>
|
||||||
<string name="menu_settings">Settings</string>
|
|
||||||
<string name="button_save_settings">Save Settings</string>
|
<string name="button_save_settings">Save Settings</string>
|
||||||
<string name="menu_add_charge">Add Charge</string>
|
|
||||||
<string name="menu_add_battery">Add Battery</string>
|
<string name="nav_header_title">Navigation</string>
|
||||||
|
<string name="nav_home">Home</string>
|
||||||
|
<string name="nav_add_charge">Add Charge</string>
|
||||||
|
<string name="nav_add_battery">Add Battery</string>
|
||||||
|
|
||||||
<string name="battery_id">Battery ID</string>
|
<string name="battery_id">Battery ID</string>
|
||||||
<string name="button_save_charge">Save Charge</string>
|
<string name="button_save_charge">Save Charge</string>
|
||||||
<string name="date">Date</string>
|
<string name="date">Date</string>
|
||||||
@@ -31,4 +29,5 @@
|
|||||||
<string name="helper_battery_not_unique">Battery-Name not unique!</string>
|
<string name="helper_battery_not_unique">Battery-Name not unique!</string>
|
||||||
<string name="helper_battery_not_found">Battery not found</string>
|
<string name="helper_battery_not_found">Battery not found</string>
|
||||||
<string name="hint_filter_batteries">Filter Batteries…</string>
|
<string name="hint_filter_batteries">Filter Batteries…</string>
|
||||||
|
<string name="title_activity_main2">MainActivity2</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
|
|
||||||
|
ext {
|
||||||
|
compose_version = '1.1.0-beta01'
|
||||||
|
}
|
||||||
}// 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.
|
||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application' version '7.2.1' apply false
|
id 'com.android.application' version '7.2.1' apply false
|
||||||
|
|||||||
Reference in New Issue
Block a user