From f637007e64c7b625a8561efaeda2690e62aa6547 Mon Sep 17 00:00:00 2001 From: sockenklaus Date: Thu, 21 Jul 2022 03:30:34 +0200 Subject: [PATCH] Moved to complete Compose layout, rewrote all composables, removed all xml layouts. Changed navigation component. --- app/build.gradle | 37 +-- .../4.json | 149 ++++++++++++ app/src/main/AndroidManifest.xml | 10 +- .../batterytracker/MainActivity.kt | 54 +---- .../batterytracker/room/BatteryTrackerDB.kt | 2 +- .../batterytracker/room/Converters.kt | 11 + .../batterytracker/room/entities/Charge.kt | 3 +- .../batterytracker/ui/BatteryTracker.kt | 150 +++++++++++++ .../ui/composables/AddBattery.kt | 79 +++++++ .../ui/composables/AddCharge.kt | 179 +++++++++++++++ .../batterytracker/ui/composables/Home.kt | 85 +++++++ .../ui/composables/{ => util}/Composables.kt | 98 +++++++- .../add_battery/AddBatteryFragment.kt | 130 ----------- .../fragments/add_charge/AddChargeFragment.kt | 212 ------------------ .../ui/fragments/home/HomeFragment.kt | 89 -------- .../ui/fragments/settings/SettingsFragment.kt | 27 --- .../fragments/settings/SettingsViewModel.kt | 7 - .../AddBatteryViewModel.kt | 3 +- .../AddChargeViewModel.kt | 8 +- .../home => models}/HomeViewModel.kt | 2 +- .../batterytracker/ui/models/MainViewModel.kt | 21 ++ app/src/main/res/layout/activity_main.xml | 26 --- app/src/main/res/layout/app_bar_main.xml | 27 --- app/src/main/res/layout/content_main.xml | 20 -- app/src/main/res/layout/fragment_settings.xml | 31 --- app/src/main/res/layout/list_item.xml | 11 - app/src/main/res/layout/nav_header_main.xml | 22 -- .../main/res/menu/activity_main_drawer.xml | 29 --- app/src/main/res/menu/main.xml | 9 - .../main/res/navigation/mobile_navigation.xml | 36 --- app/src/main/res/values/strings.xml | 15 +- build.gradle | 3 + 32 files changed, 818 insertions(+), 767 deletions(-) create mode 100644 app/schemas/com.sockenklaus.batterytracker.room.BatteryTrackerDB/4.json create mode 100644 app/src/main/java/com/sockenklaus/batterytracker/ui/BatteryTracker.kt create mode 100644 app/src/main/java/com/sockenklaus/batterytracker/ui/composables/AddBattery.kt create mode 100644 app/src/main/java/com/sockenklaus/batterytracker/ui/composables/AddCharge.kt create mode 100644 app/src/main/java/com/sockenklaus/batterytracker/ui/composables/Home.kt rename app/src/main/java/com/sockenklaus/batterytracker/ui/composables/{ => util}/Composables.kt (61%) delete mode 100644 app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_battery/AddBatteryFragment.kt delete mode 100644 app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_charge/AddChargeFragment.kt delete mode 100644 app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/home/HomeFragment.kt delete mode 100644 app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/settings/SettingsFragment.kt delete mode 100644 app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/settings/SettingsViewModel.kt rename app/src/main/java/com/sockenklaus/batterytracker/ui/{fragments/add_battery => models}/AddBatteryViewModel.kt (93%) rename app/src/main/java/com/sockenklaus/batterytracker/ui/{fragments/add_charge => models}/AddChargeViewModel.kt (91%) rename app/src/main/java/com/sockenklaus/batterytracker/ui/{fragments/home => models}/HomeViewModel.kt (85%) create mode 100644 app/src/main/java/com/sockenklaus/batterytracker/ui/models/MainViewModel.kt delete mode 100644 app/src/main/res/layout/activity_main.xml delete mode 100644 app/src/main/res/layout/app_bar_main.xml delete mode 100644 app/src/main/res/layout/content_main.xml delete mode 100644 app/src/main/res/layout/fragment_settings.xml delete mode 100644 app/src/main/res/layout/list_item.xml delete mode 100644 app/src/main/res/layout/nav_header_main.xml delete mode 100644 app/src/main/res/menu/activity_main_drawer.xml delete mode 100644 app/src/main/res/menu/main.xml delete mode 100644 app/src/main/res/navigation/mobile_navigation.xml diff --git a/app/build.gradle b/app/build.gradle index 3e32da3..6931e79 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -67,39 +67,46 @@ android { dependencies { 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-paging: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:compose-theme-adapter:1.1.14' implementation 'androidx.core:core-ktx:1.8.0' implementation 'androidx.appcompat:appcompat:1.4.2' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 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-ui-ktx:2.5.0' + implementation "androidx.navigation:navigation-compose:2.5.0" implementation 'androidx.legacy:legacy-support-v4:1.0.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' - androidTestImplementation 'androidx.compose.ui:ui-test-junit4:1.3.0-alpha01' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - debugImplementation "androidx.compose.ui:ui-test-manifest:1.1.1" + } \ No newline at end of file diff --git a/app/schemas/com.sockenklaus.batterytracker.room.BatteryTrackerDB/4.json b/app/schemas/com.sockenklaus.batterytracker.room.BatteryTrackerDB/4.json new file mode 100644 index 0000000..4776a0d --- /dev/null +++ b/app/schemas/com.sockenklaus.batterytracker.room.BatteryTrackerDB/4.json @@ -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')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a334e98..52eddea 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,16 +13,10 @@ android:supportsRtl="true" android:theme="@style/Theme.BatteryTracker" tools:targetApi="31"> - + + android:exported="true"> diff --git a/app/src/main/java/com/sockenklaus/batterytracker/MainActivity.kt b/app/src/main/java/com/sockenklaus/batterytracker/MainActivity.kt index 7d0d22d..fb0347b 100644 --- a/app/src/main/java/com/sockenklaus/batterytracker/MainActivity.kt +++ b/app/src/main/java/com/sockenklaus/batterytracker/MainActivity.kt @@ -1,56 +1,20 @@ package com.sockenklaus.batterytracker import android.os.Bundle -import android.view.Menu -import com.google.android.material.navigation.NavigationView -import androidx.navigation.findNavController -import androidx.navigation.ui.AppBarConfiguration -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 +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.core.view.WindowCompat +import com.sockenklaus.batterytracker.ui.BatteryTracker -class MainActivity : AppCompatActivity() { - - private lateinit var appBarConfiguration: AppBarConfiguration - private lateinit var binding: ActivityMainBinding - private lateinit var navController: NavController - val db: BatteryTrackerDB by lazy { BatteryTrackerDB.getInstance(this) } +class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding = ActivityMainBinding.inflate(layoutInflater) - setContentView(binding.root) + WindowCompat.setDecorFitsSystemWindows(window, true) - setSupportActionBar(binding.appBarMain.toolbar) - - 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() + setContent { + BatteryTracker() + } } } \ No newline at end of file diff --git a/app/src/main/java/com/sockenklaus/batterytracker/room/BatteryTrackerDB.kt b/app/src/main/java/com/sockenklaus/batterytracker/room/BatteryTrackerDB.kt index 756a8ed..51374ad 100644 --- a/app/src/main/java/com/sockenklaus/batterytracker/room/BatteryTrackerDB.kt +++ b/app/src/main/java/com/sockenklaus/batterytracker/room/BatteryTrackerDB.kt @@ -12,7 +12,7 @@ import com.sockenklaus.batterytracker.room.entities.Charge @Database( entities = [Charge::class, Battery::class], - version = 3 + version = 4 ) @TypeConverters(Converters::class) abstract class BatteryTrackerDB : RoomDatabase() { diff --git a/app/src/main/java/com/sockenklaus/batterytracker/room/Converters.kt b/app/src/main/java/com/sockenklaus/batterytracker/room/Converters.kt index aa70b76..010a84f 100644 --- a/app/src/main/java/com/sockenklaus/batterytracker/room/Converters.kt +++ b/app/src/main/java/com/sockenklaus/batterytracker/room/Converters.kt @@ -2,11 +2,22 @@ package com.sockenklaus.batterytracker.room import androidx.room.TypeConverter import java.time.Instant +import java.time.LocalDate import java.time.LocalDateTime import java.time.ZoneId class Converters { + @TypeConverter + fun toLocalDate(value: Long): LocalDate { + return LocalDate.ofEpochDay(value) + } + + @TypeConverter + fun toEpochDay(value: LocalDate): Long { + return value.toEpochDay() + } + @TypeConverter fun toLocalDateTime(value: Long): LocalDateTime { return LocalDateTime.ofInstant(Instant.ofEpochMilli(value), ZoneId.systemDefault()) diff --git a/app/src/main/java/com/sockenklaus/batterytracker/room/entities/Charge.kt b/app/src/main/java/com/sockenklaus/batterytracker/room/entities/Charge.kt index 2926995..f1befd4 100644 --- a/app/src/main/java/com/sockenklaus/batterytracker/room/entities/Charge.kt +++ b/app/src/main/java/com/sockenklaus/batterytracker/room/entities/Charge.kt @@ -1,6 +1,7 @@ package com.sockenklaus.batterytracker.room.entities import androidx.room.* +import java.time.LocalDate import java.time.LocalDateTime @Entity( @@ -24,7 +25,7 @@ data class Charge( @ColumnInfo(name = "battery_id") val batteryId: Int, - val date: LocalDateTime, + val date: LocalDate, @ColumnInfo(defaultValue = "") val comment: String = "", diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/BatteryTracker.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/BatteryTracker.kt new file mode 100644 index 0000000..5dab18d --- /dev/null +++ b/app/src/main/java/com/sockenklaus/batterytracker/ui/BatteryTracker.kt @@ -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() + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/composables/AddBattery.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/composables/AddBattery.kt new file mode 100644 index 0000000..30f15aa --- /dev/null +++ b/app/src/main/java/com/sockenklaus/batterytracker/ui/composables/AddBattery.kt @@ -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) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/composables/AddCharge.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/composables/AddCharge.kt new file mode 100644 index 0000000..78e6fc1 --- /dev/null +++ b/app/src/main/java/com/sockenklaus/batterytracker/ui/composables/AddCharge.kt @@ -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)}") + } +} diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/composables/Home.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/composables/Home.kt new file mode 100644 index 0000000..ab8cbf0 --- /dev/null +++ b/app/src/main/java/com/sockenklaus/batterytracker/ui/composables/Home.kt @@ -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()) + 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() + } +}*/ diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/composables/Composables.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/composables/util/Composables.kt similarity index 61% rename from app/src/main/java/com/sockenklaus/batterytracker/ui/composables/Composables.kt rename to app/src/main/java/com/sockenklaus/batterytracker/ui/composables/util/Composables.kt index d3b8067..d940a33 100644 --- a/app/src/main/java/com/sockenklaus/batterytracker/ui/composables/Composables.kt +++ b/app/src/main/java/com/sockenklaus/batterytracker/ui/composables/util/Composables.kt @@ -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.interaction.MutableInteractionSource @@ -9,18 +9,104 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.* import androidx.compose.material.TextFieldDefaults.OutlinedTextFieldDecorationBox import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.graphics.takeOrElse +import androidx.compose.ui.graphics.* import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp 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 +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( value: String, onValueChange: (String) -> Unit, @@ -56,10 +142,10 @@ fun MyOutlinedTextFieldWithSuffix( Gray500 } - @OptIn(ExperimentalMaterialApi::class) BasicTextField( value = value, - modifier = modifier.padding(top = 8.dp) + modifier = modifier + .padding(top = 8.dp) .background(colors.backgroundColor(enabled).value, shape) .defaultMinSize( diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_battery/AddBatteryFragment.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_battery/AddBatteryFragment.kt deleted file mode 100644 index 251b9fd..0000000 --- a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_battery/AddBatteryFragment.kt +++ /dev/null @@ -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()) - - 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) - } - } - }*/ -} \ No newline at end of file diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_charge/AddChargeFragment.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_charge/AddChargeFragment.kt deleted file mode 100644 index 7f9a336..0000000 --- a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_charge/AddChargeFragment.kt +++ /dev/null @@ -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()) - - 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() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/home/HomeFragment.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/home/HomeFragment.kt deleted file mode 100644 index c939cdd..0000000 --- a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/home/HomeFragment.kt +++ /dev/null @@ -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()) } - 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() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/settings/SettingsFragment.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/settings/SettingsFragment.kt deleted file mode 100644 index 8928469..0000000 --- a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/settings/SettingsFragment.kt +++ /dev/null @@ -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) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/settings/SettingsViewModel.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/settings/SettingsViewModel.kt deleted file mode 100644 index 4520869..0000000 --- a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/settings/SettingsViewModel.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.sockenklaus.batterytracker.ui.fragments.settings - -import androidx.lifecycle.ViewModel - -class SettingsViewModel : ViewModel() { - // TODO: Implement the ViewModel -} \ No newline at end of file diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_battery/AddBatteryViewModel.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/models/AddBatteryViewModel.kt similarity index 93% rename from app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_battery/AddBatteryViewModel.kt rename to app/src/main/java/com/sockenklaus/batterytracker/ui/models/AddBatteryViewModel.kt index 61752cf..8cd53bf 100644 --- a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_battery/AddBatteryViewModel.kt +++ b/app/src/main/java/com/sockenklaus/batterytracker/ui/models/AddBatteryViewModel.kt @@ -1,4 +1,4 @@ -package com.sockenklaus.batterytracker.ui.fragments.add_battery +package com.sockenklaus.batterytracker.ui.models import android.app.Application import androidx.compose.runtime.getValue @@ -16,7 +16,6 @@ import java.lang.Exception class AddBatteryViewModel(application: Application): AndroidViewModel(application) { - private val app = application private val db = BatteryTrackerDB.getInstance(application) val batteries = db.batteryDao().getBatteries().asLiveData() diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_charge/AddChargeViewModel.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/models/AddChargeViewModel.kt similarity index 91% rename from app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_charge/AddChargeViewModel.kt rename to app/src/main/java/com/sockenklaus/batterytracker/ui/models/AddChargeViewModel.kt index 37904f2..2ecbf00 100644 --- a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_charge/AddChargeViewModel.kt +++ b/app/src/main/java/com/sockenklaus/batterytracker/ui/models/AddChargeViewModel.kt @@ -1,4 +1,4 @@ -package com.sockenklaus.batterytracker.ui.fragments.add_charge +package com.sockenklaus.batterytracker.ui.models import android.app.Application import androidx.compose.runtime.getValue @@ -15,7 +15,7 @@ import com.sockenklaus.batterytracker.room.entities.Battery import com.sockenklaus.batterytracker.room.entities.Charge import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import java.time.LocalDateTime +import java.time.LocalDate class AddChargeViewModel(application: Application) : AndroidViewModel(application) { @@ -23,7 +23,7 @@ class AddChargeViewModel(application: Application) : AndroidViewModel(applicatio var batteries: LiveData> = db.batteryDao().getBatteries().asLiveData() var batteryId by mutableStateOf(TextFieldValue("")) - var date: LocalDateTime by mutableStateOf(LocalDateTime.now()) + var date: LocalDate by mutableStateOf(LocalDate.now()) var charge by mutableStateOf("") var batteryHasError by mutableStateOf(false) @@ -35,7 +35,7 @@ class AddChargeViewModel(application: Application) : AndroidViewModel(applicatio batteryList: List, batteryName: String, charge: String, - date: LocalDateTime + date: LocalDate ):Boolean { val battery = batteryList.find { it.name == batteryName } diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/home/HomeViewModel.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/models/HomeViewModel.kt similarity index 85% rename from app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/home/HomeViewModel.kt rename to app/src/main/java/com/sockenklaus/batterytracker/ui/models/HomeViewModel.kt index 63bf22c..112ac66 100644 --- a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/home/HomeViewModel.kt +++ b/app/src/main/java/com/sockenklaus/batterytracker/ui/models/HomeViewModel.kt @@ -1,4 +1,4 @@ -package com.sockenklaus.batterytracker.ui.fragments.home +package com.sockenklaus.batterytracker.ui.models import android.app.Application import androidx.lifecycle.* diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/models/MainViewModel.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/models/MainViewModel.kt new file mode 100644 index 0000000..ddb5cae --- /dev/null +++ b/app/src/main/java/com/sockenklaus/batterytracker/ui/models/MainViewModel.kt @@ -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() + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index 3615eaa..0000000 --- a/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/app_bar_main.xml b/app/src/main/res/layout/app_bar_main.xml deleted file mode 100644 index 3f2bd1e..0000000 --- a/app/src/main/res/layout/app_bar_main.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml deleted file mode 100644 index 6e0ea39..0000000 --- a/app/src/main/res/layout/content_main.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml deleted file mode 100644 index c496fd6..0000000 --- a/app/src/main/res/layout/fragment_settings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/list_item.xml b/app/src/main/res/layout/list_item.xml deleted file mode 100644 index b3d58f4..0000000 --- a/app/src/main/res/layout/list_item.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/nav_header_main.xml b/app/src/main/res/layout/nav_header_main.xml deleted file mode 100644 index 37881c1..0000000 --- a/app/src/main/res/layout/nav_header_main.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml deleted file mode 100644 index d9275fd..0000000 --- a/app/src/main/res/menu/activity_main_drawer.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/main.xml b/app/src/main/res/menu/main.xml deleted file mode 100644 index 4b177d2..0000000 --- a/app/src/main/res/menu/main.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml deleted file mode 100644 index 5d3fb35..0000000 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f076b80..ed933cb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,21 +1,19 @@ BatteryTracker - Open navigation drawer - Close navigation drawer - Navigation android.studio@android.com Navigation header Settings - Home - Gallery Slideshow Hello blank fragment - Settings Save Settings - Add Charge - Add Battery + + Navigation + Home + Add Charge + Add Battery + Battery ID Save Charge Date @@ -31,4 +29,5 @@ Battery-Name not unique! Battery not found Filter Batteries… + MainActivity2 \ No newline at end of file diff --git a/build.gradle b/build.gradle index d74ff6b..8717394 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,8 @@ buildscript { +ext { + compose_version = '1.1.0-beta01' +} }// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { id 'com.android.application' version '7.2.1' apply false