First commit to Database!!!

This commit is contained in:
sockenklaus
2022-07-17 02:23:24 +02:00
parent f22aa22a3f
commit 089f09b1bd
13 changed files with 376 additions and 27 deletions

View File

@@ -1,6 +1,9 @@
plugins { plugins {
id 'com.android.application' id 'com.android.application'
id 'org.jetbrains.kotlin.android' id 'org.jetbrains.kotlin.android'
id 'org.jetbrains.kotlin.kapt'
} }
android { android {
@@ -17,6 +20,16 @@ android {
vectorDrawables { vectorDrawables {
useSupportLibrary true useSupportLibrary true
} }
javaCompileOptions {
annotationProcessorOptions {
arguments += [
"room.schemaLocation":"$projectDir/schemas".toString(),
"room.incremental":"true",
"room.expandProjection":"true"]
}
}
} }
buildTypes { buildTypes {
@@ -48,15 +61,17 @@ android {
} }
dependencies { 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:1.3.0-alpha01"
implementation "androidx.compose.ui:ui-tooling-preview: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-icons-extended:1.3.0-alpha01'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.0' 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' implementation 'com.google.android.material:material:1.7.0-alpha02'

View File

@@ -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')"
]
}
}

View File

@@ -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')"
]
}
}

View File

@@ -12,12 +12,14 @@ import androidx.drawerlayout.widget.DrawerLayout
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.NavController import androidx.navigation.NavController
import com.sockenklaus.batterytracker.databinding.ActivityMainBinding import com.sockenklaus.batterytracker.databinding.ActivityMainBinding
import com.sockenklaus.batterytracker.room.BatteryTrackerDB
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var binding: ActivityMainBinding private lateinit var binding: ActivityMainBinding
private lateinit var navController: NavController 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)

View File

@@ -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
}
}
}
}

View File

@@ -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<List<Battery>>
@Query("Select * FROM batteries WHERE id = :id")
fun getBatteryById(id: String): Flow<Battery>
}

View File

@@ -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<List<Charge>>
@Query("Select * FROM charges WHERE id = :id")
fun getChargeById(id: Int): Flow<Charge>
}

View File

@@ -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?,
)

View File

@@ -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
)

View File

@@ -21,10 +21,15 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension import androidx.constraintlayout.compose.Dimension
import androidx.lifecycle.lifecycleScope
import com.sockenklaus.batterytracker.R import com.sockenklaus.batterytracker.R
import com.sockenklaus.batterytracker.databinding.FragmentAddBatteryBinding 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.ui.theme.BatteryTrackerTheme
import com.sockenklaus.batterytracker.util.validateDecimal import com.sockenklaus.batterytracker.util.validateDecimal
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
/** /**
* A simple [Fragment] subclass. * A simple [Fragment] subclass.
@@ -86,7 +91,9 @@ class AddBatteryFragment : Fragment() {
) )
ExtendedFloatingActionButton( ExtendedFloatingActionButton(
onClick = { saveBattery() }, onClick = { saveBattery(
batteryId, declaredCapacity
) },
icon = { Icon(Icons.Default.Save, null) }, icon = { Icon(Icons.Default.Save, null) },
text = { Text(stringResource(R.string.button_save_battery)) }, text = { Text(stringResource(R.string.button_save_battery)) },
modifier = Modifier modifier = Modifier
@@ -108,8 +115,12 @@ class AddBatteryFragment : Fragment() {
/* /*
TODO: Implement the action 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 { /*companion object {

View File

@@ -25,6 +25,7 @@ import androidx.compose.ui.unit.dp
import com.google.android.material.datepicker.MaterialDatePicker import com.google.android.material.datepicker.MaterialDatePicker
import com.sockenklaus.batterytracker.R import com.sockenklaus.batterytracker.R
import com.sockenklaus.batterytracker.databinding.FragmentAddChargeBinding import com.sockenklaus.batterytracker.databinding.FragmentAddChargeBinding
import com.sockenklaus.batterytracker.room.entities.Battery
import com.sockenklaus.batterytracker.ui.theme.BatteryTrackerTheme import com.sockenklaus.batterytracker.ui.theme.BatteryTrackerTheme
import com.sockenklaus.batterytracker.util.validateDecimal import com.sockenklaus.batterytracker.util.validateDecimal
import java.time.* import java.time.*
@@ -47,18 +48,17 @@ class AddChargeFragment : Fragment() {
_binding = FragmentAddChargeBinding.inflate(inflater, container, false) _binding = FragmentAddChargeBinding.inflate(inflater, container, false)
viewModel = ViewModelProvider(this)[AddChargeViewModel::class.java] viewModel = ViewModelProvider(this)[AddChargeViewModel::class.java]
var items = emptyList<Battery>()
// TODO: Add viewModel!! viewModel.allBatteryIds.observe(this.viewLifecycleOwner){
val items = listOf("E1", "E2", "E3", "E4", "Bonsai", "Bonai") items = it
}
binding.root.setContent{ binding.root.setContent{
BatteryTrackerTheme { BatteryTrackerTheme {
val outerPadding = 24.dp val outerPadding = 24.dp
val innerPadding = 16.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)} var bIdExpanded by remember { mutableStateOf(false)}
Column( Column(
@@ -69,8 +69,8 @@ class AddChargeFragment : Fragment() {
onExpandedChange = { bIdExpanded = !bIdExpanded} onExpandedChange = { bIdExpanded = !bIdExpanded}
) { ) {
OutlinedTextField( OutlinedTextField(
value = selectedBatteryId, value = viewModel.batteryId,
onValueChange = { selectedBatteryId = it }, onValueChange = { viewModel.batteryId = it },
label = { Text(stringResource(R.string.select_battery_id)) }, label = { Text(stringResource(R.string.select_battery_id)) },
leadingIcon = { Icon(Icons.Default.Tag, null) }, leadingIcon = { Icon(Icons.Default.Tag, null) },
trailingIcon = { trailingIcon = {
@@ -79,8 +79,8 @@ class AddChargeFragment : Fragment() {
colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(), colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(),
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
val filteringOptions = items.filter { it.contains(selectedBatteryId.text, ignoreCase = true)} val filteringOptions = items.filter { it.id.contains(viewModel.batteryId.text, ignoreCase = true)}
if(filteringOptions.size > 1 && selectedBatteryId.text.isNotBlank()) bIdExpanded = true if(filteringOptions.size > 1 && viewModel.batteryId.text.isNotBlank()) bIdExpanded = true
if(filteringOptions.isNotEmpty()) { if(filteringOptions.isNotEmpty()) {
ExposedDropdownMenu( ExposedDropdownMenu(
expanded = bIdExpanded, expanded = bIdExpanded,
@@ -89,14 +89,14 @@ class AddChargeFragment : Fragment() {
for (filteringOption in filteringOptions) { for (filteringOption in filteringOptions) {
DropdownMenuItem( DropdownMenuItem(
onClick = { onClick = {
selectedBatteryId = TextFieldValue( viewModel.batteryId = TextFieldValue(
text = filteringOption, text = filteringOption.id,
selection = TextRange(filteringOption.length) selection = TextRange(filteringOption.id.length)
) )
bIdExpanded = false bIdExpanded = false
} }
) { ) {
Text(filteringOption) Text(filteringOption.id)
} }
} }
} }
@@ -106,8 +106,8 @@ class AddChargeFragment : Fragment() {
Spacer(Modifier.size(innerPadding)) Spacer(Modifier.size(innerPadding))
OutlinedTextField( OutlinedTextField(
value = charge, value = viewModel.charge,
onValueChange = { charge = validateDecimal(it, charge) }, onValueChange = { viewModel.charge = validateDecimal(it, viewModel.charge) },
leadingIcon = { Icon(Icons.Default.BatteryChargingFull, "Icon Battery Charging Full") }, leadingIcon = { Icon(Icons.Default.BatteryChargingFull, "Icon Battery Charging Full") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
label = { Text(stringResource(R.string.hint_charge)) }, label = { Text(stringResource(R.string.hint_charge)) },
@@ -117,15 +117,15 @@ class AddChargeFragment : Fragment() {
Spacer(Modifier.size(innerPadding)) Spacer(Modifier.size(innerPadding))
ChargeDatePicker( ChargeDatePicker(
date = date, date = viewModel.date,
onSelect = { date = it} onSelect = { viewModel.date = it}
) )
Spacer(Modifier.size(outerPadding)) Spacer(Modifier.size(outerPadding))
ExtendedFloatingActionButton( ExtendedFloatingActionButton(
text = { Text(stringResource(R.string.button_save_charge)) }, text = { Text(stringResource(R.string.button_save_charge)) },
onClick = { /*TODO*/ }, onClick = { viewModel.saveCharge() },
icon = { Icon(Icons.Default.Save, "Icon Save") }, icon = { Icon(Icons.Default.Save, "Icon Save") },
modifier = Modifier.align(Alignment.End) modifier = Modifier.align(Alignment.End)

View File

@@ -1,7 +1,29 @@
package com.sockenklaus.batterytracker.ui.fragments.add_charge 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() { class AddChargeViewModel(application: Application) : AndroidViewModel(application) {
// TODO: Implement the ViewModel
private val db = BatteryTrackerDB.getInstance(application)
var allBatteryIds: LiveData<List<Battery>> = db.batteryDao().getBatteries().asLiveData()
var batteryId: TextFieldValue by mutableStateOf(TextFieldValue(""))
var date: LocalDateTime by mutableStateOf(LocalDateTime.now())
var charge: String by mutableStateOf("")
fun saveCharge() {
}
} }

View File

@@ -5,6 +5,7 @@ plugins {
id 'com.android.application' version '7.2.1' apply false id 'com.android.application' version '7.2.1' apply false
id 'com.android.library' 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.android' version '1.7.0' apply false
id 'org.jetbrains.kotlin.kapt' version '1.7.0' apply false
} }
task clean(type: Delete) { task clean(type: Delete) {