From 089f09b1bd9a5c46cace5173a3e4cc05ca41d2d6 Mon Sep 17 00:00:00 2001 From: sockenklaus Date: Sun, 17 Jul 2022 02:23:24 +0200 Subject: [PATCH] First commit to Database!!! --- app/build.gradle | 23 ++++- .../1.json | 85 +++++++++++++++++++ .../2.json | 85 +++++++++++++++++++ .../batterytracker/MainActivity.kt | 2 + .../batterytracker/room/BatteryTrackerDB.kt | 39 +++++++++ .../batterytracker/room/dao/BatteryDao.kt | 26 ++++++ .../batterytracker/room/dao/ChargeDao.kt | 26 ++++++ .../batterytracker/room/entities/Battery.kt | 14 +++ .../batterytracker/room/entities/Charge.kt | 23 +++++ .../ui/fragments/AddBatteryFragment.kt | 15 +++- .../fragments/add_charge/AddChargeFragment.kt | 36 ++++---- .../add_charge/AddChargeViewModel.kt | 28 +++++- build.gradle | 1 + 13 files changed, 376 insertions(+), 27 deletions(-) create mode 100644 app/schemas/com.sockenklaus.batterytracker.room.BatteryTrackerDB/1.json create mode 100644 app/schemas/com.sockenklaus.batterytracker.room.BatteryTrackerDB/2.json create mode 100644 app/src/main/java/com/sockenklaus/batterytracker/room/BatteryTrackerDB.kt create mode 100644 app/src/main/java/com/sockenklaus/batterytracker/room/dao/BatteryDao.kt create mode 100644 app/src/main/java/com/sockenklaus/batterytracker/room/dao/ChargeDao.kt create mode 100644 app/src/main/java/com/sockenklaus/batterytracker/room/entities/Battery.kt create mode 100644 app/src/main/java/com/sockenklaus/batterytracker/room/entities/Charge.kt diff --git a/app/build.gradle b/app/build.gradle index 041ef6b..7d39656 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,9 @@ + + plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' + id 'org.jetbrains.kotlin.kapt' } android { @@ -17,6 +20,16 @@ android { vectorDrawables { useSupportLibrary true } + + javaCompileOptions { + annotationProcessorOptions { + arguments += [ + "room.schemaLocation":"$projectDir/schemas".toString(), + "room.incremental":"true", + "room.expandProjection":"true"] + } + } + } buildTypes { @@ -48,15 +61,17 @@ android { } dependencies { - implementation 'androidx.room:room-runtime:2.5.0-alpha02' + 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' - annotationProcessor 'androidx.room:room-compiler:2.5.0-alpha02' + 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" - implementation 'androidx.room:room-rxjava3:2.5.0-alpha02' - implementation "androidx.room:room-paging:2.5.0-alpha02" implementation 'com.google.android.material:material:1.7.0-alpha02' diff --git a/app/schemas/com.sockenklaus.batterytracker.room.BatteryTrackerDB/1.json b/app/schemas/com.sockenklaus.batterytracker.room.BatteryTrackerDB/1.json new file mode 100644 index 0000000..9abec71 --- /dev/null +++ b/app/schemas/com.sockenklaus.batterytracker.room.BatteryTrackerDB/1.json @@ -0,0 +1,85 @@ +{ + "formatVersion": 1, + "database": { + "version": 1, + "identityHash": "4f4222f215c1724a7401aa8d181012a4", + "entities": [ + { + "tableName": "charges", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `charge` REAL NOT NULL, `battery_id` TEXT NOT NULL, `date` TEXT NOT NULL, `createdTime` TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "charge", + "columnName": "charge", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "batteryId", + "columnName": "battery_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdTime", + "columnName": "createdTime", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "batteries", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `declared_charge` REAL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "declaredCharge", + "columnName": "declared_charge", + "affinity": "REAL", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "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, '4f4222f215c1724a7401aa8d181012a4')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/com.sockenklaus.batterytracker.room.BatteryTrackerDB/2.json b/app/schemas/com.sockenklaus.batterytracker.room.BatteryTrackerDB/2.json new file mode 100644 index 0000000..984643f --- /dev/null +++ b/app/schemas/com.sockenklaus.batterytracker.room.BatteryTrackerDB/2.json @@ -0,0 +1,85 @@ +{ + "formatVersion": 1, + "database": { + "version": 2, + "identityHash": "4f4222f215c1724a7401aa8d181012a4", + "entities": [ + { + "tableName": "charges", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `charge` REAL NOT NULL, `battery_id` TEXT NOT NULL, `date` TEXT NOT NULL, `createdTime` TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "charge", + "columnName": "charge", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "batteryId", + "columnName": "battery_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdTime", + "columnName": "createdTime", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "batteries", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `declared_charge` REAL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "declaredCharge", + "columnName": "declared_charge", + "affinity": "REAL", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "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, '4f4222f215c1724a7401aa8d181012a4')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sockenklaus/batterytracker/MainActivity.kt b/app/src/main/java/com/sockenklaus/batterytracker/MainActivity.kt index 5e18229..7d0d22d 100644 --- a/app/src/main/java/com/sockenklaus/batterytracker/MainActivity.kt +++ b/app/src/main/java/com/sockenklaus/batterytracker/MainActivity.kt @@ -12,12 +12,14 @@ 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() { 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?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/sockenklaus/batterytracker/room/BatteryTrackerDB.kt b/app/src/main/java/com/sockenklaus/batterytracker/room/BatteryTrackerDB.kt new file mode 100644 index 0000000..8e3c4ca --- /dev/null +++ b/app/src/main/java/com/sockenklaus/batterytracker/room/BatteryTrackerDB.kt @@ -0,0 +1,39 @@ +package com.sockenklaus.batterytracker.room + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import com.sockenklaus.batterytracker.room.dao.BatteryDao +import com.sockenklaus.batterytracker.room.dao.ChargeDao +import com.sockenklaus.batterytracker.room.entities.Battery +import com.sockenklaus.batterytracker.room.entities.Charge + +@Database( + entities = [Charge::class, Battery::class], + version = 2 +) +abstract class BatteryTrackerDB : RoomDatabase() { + abstract fun batteryDao(): BatteryDao + + abstract fun chargeDao(): ChargeDao + + companion object { + @Volatile + private var INSTANCE: BatteryTrackerDB? = null + + fun getInstance(context: Context): BatteryTrackerDB { + return INSTANCE ?: synchronized(this) { + val instance = Room.databaseBuilder( + context.applicationContext, + BatteryTrackerDB::class.java, + "battery_tracker_db" + ) + .fallbackToDestructiveMigration() + .build() + INSTANCE = instance + return instance + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sockenklaus/batterytracker/room/dao/BatteryDao.kt b/app/src/main/java/com/sockenklaus/batterytracker/room/dao/BatteryDao.kt new file mode 100644 index 0000000..22b0c7e --- /dev/null +++ b/app/src/main/java/com/sockenklaus/batterytracker/room/dao/BatteryDao.kt @@ -0,0 +1,26 @@ +package com.sockenklaus.batterytracker.room.dao + +import androidx.room.* +import com.sockenklaus.batterytracker.room.entities.Battery +import kotlinx.coroutines.flow.Flow + +@Dao +interface BatteryDao { + @Insert + fun insertAll(vararg batteries: Battery) + + @Insert + fun insert(battery: Battery) + + @Delete + fun delete(battery: Battery) + + @Update + fun updateBatteries(vararg batteries: Battery): Int + + @Query("Select * FROM batteries") + fun getBatteries(): Flow> + + @Query("Select * FROM batteries WHERE id = :id") + fun getBatteryById(id: String): Flow +} \ No newline at end of file diff --git a/app/src/main/java/com/sockenklaus/batterytracker/room/dao/ChargeDao.kt b/app/src/main/java/com/sockenklaus/batterytracker/room/dao/ChargeDao.kt new file mode 100644 index 0000000..e98070f --- /dev/null +++ b/app/src/main/java/com/sockenklaus/batterytracker/room/dao/ChargeDao.kt @@ -0,0 +1,26 @@ +package com.sockenklaus.batterytracker.room.dao + +import androidx.room.* +import com.sockenklaus.batterytracker.room.entities.Charge +import kotlinx.coroutines.flow.Flow + +@Dao +interface ChargeDao { + @Insert + fun insertAll(vararg charges: Charge) + + @Insert + fun insert(charge: Charge) + + @Delete + fun delete(charge: Charge): Int + + @Update + fun updateCharges(vararg charges: Charge): Int + + @Query("Select * FROM charges") + fun getCharges(): Flow> + + @Query("Select * FROM charges WHERE id = :id") + fun getChargeById(id: Int): Flow +} \ No newline at end of file diff --git a/app/src/main/java/com/sockenklaus/batterytracker/room/entities/Battery.kt b/app/src/main/java/com/sockenklaus/batterytracker/room/entities/Battery.kt new file mode 100644 index 0000000..8a4a785 --- /dev/null +++ b/app/src/main/java/com/sockenklaus/batterytracker/room/entities/Battery.kt @@ -0,0 +1,14 @@ +package com.sockenklaus.batterytracker.room.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "batteries") +data class Battery( + @PrimaryKey + val id: String, + + @ColumnInfo(name = "declared_charge") + val declaredCharge: Double?, +) 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 new file mode 100644 index 0000000..1374869 --- /dev/null +++ b/app/src/main/java/com/sockenklaus/batterytracker/room/entities/Charge.kt @@ -0,0 +1,23 @@ +package com.sockenklaus.batterytracker.room.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.time.LocalDateTime + +@Entity(tableName = "charges") +data class Charge( + + @PrimaryKey + val id: Int, + + val charge: Double, + + @ColumnInfo(name = "battery_id") + val batteryId: String, + + val date: String, + + @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") + val createdTime: String +) diff --git a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/AddBatteryFragment.kt b/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/AddBatteryFragment.kt index 8c64e21..188e566 100644 --- a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/AddBatteryFragment.kt +++ b/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/AddBatteryFragment.kt @@ -21,10 +21,15 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension +import androidx.lifecycle.lifecycleScope import com.sockenklaus.batterytracker.R import com.sockenklaus.batterytracker.databinding.FragmentAddBatteryBinding +import com.sockenklaus.batterytracker.room.BatteryTrackerDB +import com.sockenklaus.batterytracker.room.entities.Battery import com.sockenklaus.batterytracker.ui.theme.BatteryTrackerTheme import com.sockenklaus.batterytracker.util.validateDecimal +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch /** * A simple [Fragment] subclass. @@ -86,7 +91,9 @@ class AddBatteryFragment : Fragment() { ) ExtendedFloatingActionButton( - onClick = { saveBattery() }, + onClick = { saveBattery( + batteryId, declaredCapacity + ) }, icon = { Icon(Icons.Default.Save, null) }, text = { Text(stringResource(R.string.button_save_battery)) }, modifier = Modifier @@ -108,8 +115,12 @@ class AddBatteryFragment : Fragment() { /* TODO: Implement the action */ - private fun saveBattery() { + private fun saveBattery(id: String, declaredCapacity: String) { + val db = BatteryTrackerDB.getInstance(requireContext()) + lifecycleScope.launch(Dispatchers.IO){ + db.batteryDao().insert(Battery(id, declaredCapacity.toDouble())) + } } /*companion object { 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 index 2344fef..a1cc6f6 100644 --- 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 @@ -25,6 +25,7 @@ import androidx.compose.ui.unit.dp import com.google.android.material.datepicker.MaterialDatePicker import com.sockenklaus.batterytracker.R import com.sockenklaus.batterytracker.databinding.FragmentAddChargeBinding +import com.sockenklaus.batterytracker.room.entities.Battery import com.sockenklaus.batterytracker.ui.theme.BatteryTrackerTheme import com.sockenklaus.batterytracker.util.validateDecimal import java.time.* @@ -47,18 +48,17 @@ class AddChargeFragment : Fragment() { _binding = FragmentAddChargeBinding.inflate(inflater, container, false) viewModel = ViewModelProvider(this)[AddChargeViewModel::class.java] + var items = emptyList() - // TODO: Add viewModel!! - val items = listOf("E1", "E2", "E3", "E4", "Bonsai", "Bonai") + viewModel.allBatteryIds.observe(this.viewLifecycleOwner){ + items = it + } binding.root.setContent{ BatteryTrackerTheme { val outerPadding = 24.dp val innerPadding = 16.dp - var selectedBatteryId by remember { mutableStateOf(TextFieldValue(""))} - var date by remember { mutableStateOf(LocalDateTime.now())} - var charge by remember { mutableStateOf("") } var bIdExpanded by remember { mutableStateOf(false)} Column( @@ -69,8 +69,8 @@ class AddChargeFragment : Fragment() { onExpandedChange = { bIdExpanded = !bIdExpanded} ) { OutlinedTextField( - value = selectedBatteryId, - onValueChange = { selectedBatteryId = it }, + value = viewModel.batteryId, + onValueChange = { viewModel.batteryId = it }, label = { Text(stringResource(R.string.select_battery_id)) }, leadingIcon = { Icon(Icons.Default.Tag, null) }, trailingIcon = { @@ -79,8 +79,8 @@ class AddChargeFragment : Fragment() { colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(), modifier = Modifier.fillMaxWidth() ) - val filteringOptions = items.filter { it.contains(selectedBatteryId.text, ignoreCase = true)} - if(filteringOptions.size > 1 && selectedBatteryId.text.isNotBlank()) bIdExpanded = true + val filteringOptions = items.filter { it.id.contains(viewModel.batteryId.text, ignoreCase = true)} + if(filteringOptions.size > 1 && viewModel.batteryId.text.isNotBlank()) bIdExpanded = true if(filteringOptions.isNotEmpty()) { ExposedDropdownMenu( expanded = bIdExpanded, @@ -89,14 +89,14 @@ class AddChargeFragment : Fragment() { for (filteringOption in filteringOptions) { DropdownMenuItem( onClick = { - selectedBatteryId = TextFieldValue( - text = filteringOption, - selection = TextRange(filteringOption.length) + viewModel.batteryId = TextFieldValue( + text = filteringOption.id, + selection = TextRange(filteringOption.id.length) ) bIdExpanded = false } ) { - Text(filteringOption) + Text(filteringOption.id) } } } @@ -106,8 +106,8 @@ class AddChargeFragment : Fragment() { Spacer(Modifier.size(innerPadding)) OutlinedTextField( - value = charge, - onValueChange = { charge = validateDecimal(it, charge) }, + value = viewModel.charge, + onValueChange = { viewModel.charge = validateDecimal(it, viewModel.charge) }, leadingIcon = { Icon(Icons.Default.BatteryChargingFull, "Icon Battery Charging Full") }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal), label = { Text(stringResource(R.string.hint_charge)) }, @@ -117,15 +117,15 @@ class AddChargeFragment : Fragment() { Spacer(Modifier.size(innerPadding)) ChargeDatePicker( - date = date, - onSelect = { date = it} + date = viewModel.date, + onSelect = { viewModel.date = it} ) Spacer(Modifier.size(outerPadding)) ExtendedFloatingActionButton( text = { Text(stringResource(R.string.button_save_charge)) }, - onClick = { /*TODO*/ }, + onClick = { viewModel.saveCharge() }, icon = { Icon(Icons.Default.Save, "Icon Save") }, modifier = Modifier.align(Alignment.End) 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/fragments/add_charge/AddChargeViewModel.kt index 6163b0d..da89511 100644 --- a/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_charge/AddChargeViewModel.kt +++ b/app/src/main/java/com/sockenklaus/batterytracker/ui/fragments/add_charge/AddChargeViewModel.kt @@ -1,7 +1,29 @@ package com.sockenklaus.batterytracker.ui.fragments.add_charge -import androidx.lifecycle.ViewModel +import android.app.Application +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.text.input.TextFieldValue +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.asLiveData +import androidx.lifecycle.MutableLiveData +import androidx.room.Room +import com.sockenklaus.batterytracker.room.BatteryTrackerDB +import com.sockenklaus.batterytracker.room.entities.Battery +import java.time.LocalDateTime -class AddChargeViewModel : ViewModel() { - // TODO: Implement the ViewModel +class AddChargeViewModel(application: Application) : AndroidViewModel(application) { + + private val db = BatteryTrackerDB.getInstance(application) + var allBatteryIds: LiveData> = db.batteryDao().getBatteries().asLiveData() + + var batteryId: TextFieldValue by mutableStateOf(TextFieldValue("")) + var date: LocalDateTime by mutableStateOf(LocalDateTime.now()) + var charge: String by mutableStateOf("") + + fun saveCharge() { + + } } \ No newline at end of file diff --git a/build.gradle b/build.gradle index 728c6c6..d74ff6b 100644 --- a/build.gradle +++ b/build.gradle @@ -5,6 +5,7 @@ plugins { id 'com.android.application' version '7.2.1' apply false id 'com.android.library' version '7.2.1' apply false id 'org.jetbrains.kotlin.android' version '1.7.0' apply false + id 'org.jetbrains.kotlin.kapt' version '1.7.0' apply false } task clean(type: Delete) {