Data binding and Room for storage not working yet

This commit is contained in:
Mathieu Sanchez 2020-04-14 09:35:24 +02:00
parent 6c997af508
commit 6903a584fc
12 changed files with 436 additions and 190 deletions

View File

@ -1,11 +1,21 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android-extensions'
android { android {
compileSdkVersion 29 compileSdkVersion 29
buildToolsVersion "29.0.3" buildToolsVersion "29.0.3"
dataBinding {
enabled = true
}
configurations.all() {
resolutionStrategy.force "org.antlr:antlr4-runtime:4.5.3"
resolutionStrategy.force "org.antlr:antlr4-tool:4.5.3"
}
defaultConfig { defaultConfig {
applicationId "fr.sanchezm.attestationsCovid19" applicationId "fr.sanchezm.attestationsCovid19"
minSdkVersion 23 minSdkVersion 23
@ -26,7 +36,6 @@ android {
// To inline the bytecode built with JVM target 1.8 into // To inline the bytecode built with JVM target 1.8 into
// bytecode that is being built with JVM target 1.6. (e.g. navArgs) // bytecode that is being built with JVM target 1.6. (e.g. navArgs)
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
@ -46,10 +55,21 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.navigation:navigation-fragment:2.2.1' implementation 'androidx.navigation:navigation-fragment:2.2.1'
implementation 'androidx.navigation:navigation-ui:2.2.1' implementation 'androidx.navigation:navigation-ui:2.2.1'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.navigation:navigation-fragment-ktx:2.2.1' implementation 'androidx.navigation:navigation-fragment-ktx:2.2.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.2.1' implementation 'androidx.navigation:navigation-ui-ktx:2.2.1'
// Room
implementation 'androidx.room:room-runtime:2.2.5'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
kapt 'androidx.room:room-compiler:2.2.5'
// kapt 'com.android.databinding:compiler:3.1.4'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
} }

View File

@ -11,11 +11,11 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
val navView: BottomNavigationView = findViewById(R.id.nav_view) val navView: BottomNavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment) val navController = findNavController(R.id.nav_host_fragment)
navController
actionBar?.hide() actionBar?.hide()
navView.setupWithNavController(navController) navView.setupWithNavController(navController)
navView.setBackgroundColor(resources.getColor(R.color.itemBackground, theme)) navView.setBackgroundColor(resources.getColor(R.color.itemBackground, theme))

View File

@ -0,0 +1,34 @@
package fr.sanchezm.attestationsCovid19.data.db
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import fr.sanchezm.attestationsCovid19.data.db.dao.ProfileDao
import fr.sanchezm.attestationsCovid19.data.db.entity.Profile
@Database(
entities = [Profile::class],
version = 1
)
abstract class MyDatabase private constructor() : RoomDatabase() {
abstract fun profileDao(): ProfileDao
companion object {
@Volatile
private var instance: MyDatabase? = null
private val LOCK = Any()
operator fun invoke(context: Context) = instance ?: synchronized(LOCK) {
instance ?: buildDatabase(context).also { instance = it }
}
private fun buildDatabase(context: Context) =
Room.databaseBuilder(
context.applicationContext,
MyDatabase::class.java, "data.db"
)
.build()
}
}

View File

@ -0,0 +1,18 @@
package fr.sanchezm.attestationsCovid19.data.db.dao
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import fr.sanchezm.attestationsCovid19.data.db.entity.CURRENT_PROFILE_ID
import fr.sanchezm.attestationsCovid19.data.db.entity.Profile
@Dao
interface ProfileDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(profile: Profile)
@Query(value = "SELECT * FROM profile WHERE id = $CURRENT_PROFILE_ID")
fun getProfile(): LiveData<Profile>
}

View File

@ -0,0 +1,29 @@
package fr.sanchezm.attestationsCovid19.data.db.entity
import androidx.room.Entity
import androidx.room.PrimaryKey
const val CURRENT_PROFILE_ID = 0
@Entity(tableName = "profile")
data class Profile(
val firstName: String,
val lastName: String,
val birthday: String,
val birthPlace: String,
val address: String,
val city: String,
val postalCode: String
) {
@PrimaryKey
var id: Int = CURRENT_PROFILE_ID
override fun toString(): String {
val builder = StringBuilder()
builder.append("Nom:", lastName, "; ")
builder.append("Prenom:", firstName, "; ")
builder.append("Naissance:", birthday, " a ", birthPlace, "; ")
builder.append("Adresse:", address, " ", postalCode, " ", city, "; ")
return builder.toString()
}
}

View File

@ -0,0 +1,21 @@
package fr.sanchezm.attestationsCovid19.data.repository
import fr.sanchezm.attestationsCovid19.data.db.dao.ProfileDao
import fr.sanchezm.attestationsCovid19.data.db.entity.Profile
class ProfileRepository private constructor(private val profileDao: ProfileDao) {
fun getProfile() = profileDao.getProfile()
fun insertProfile(profile: Profile) = profileDao.insert(profile)
companion object {
@Volatile
private var instance: ProfileRepository? = null
fun getInstance(profileDao: ProfileDao) =
instance ?: synchronized(this) {
instance ?: ProfileRepository(profileDao).also { instance = it }
}
}
}

View File

@ -4,25 +4,41 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProviders
import fr.sanchezm.attestationsCovid19.R import fr.sanchezm.attestationsCovid19.R
import fr.sanchezm.attestationsCovid19.databinding.FragmentAddAttestationBinding
import fr.sanchezm.attestationsCovid19.utilities.InjectorUtils
class AddFragment : Fragment() { class AddFragment : Fragment() {
private lateinit var homeViewModel: AddViewModel private lateinit var addViewModel: AddViewModel
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
homeViewModel = initializeUi()
ViewModelProviders.of(this).get(AddViewModel::class.java) val binding = DataBindingUtil.inflate<FragmentAddAttestationBinding>(
val root = inflater.inflate(R.layout.fragment_add_attestation, container, false) inflater,
homeViewModel.text.observe(viewLifecycleOwner, Observer { R.layout.fragment_add_attestation,
}) container,
return root false
).apply {
this.lifecycleOwner = this@AddFragment
this.viewModel = addViewModel
}
return binding.root
}
private fun initializeUi() {
val factory = context?.let { InjectorUtils.provideAddViewModelFactory(it) }
addViewModel = factory?.let {
ViewModelProvider(this, it)
.get(AddViewModel::class.java)
}!!
} }
} }

View File

@ -1,13 +1,69 @@
package fr.sanchezm.attestationsCovid19.ui.add package fr.sanchezm.attestationsCovid19.ui.add
import androidx.lifecycle.LiveData import android.annotation.SuppressLint
import android.util.Log
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import fr.sanchezm.attestationsCovid19.data.db.entity.Profile
import fr.sanchezm.attestationsCovid19.data.repository.ProfileRepository
class AddViewModel : ViewModel() { class AddViewModel(private val profileRepository: ProfileRepository) : ViewModel() {
private val _text = MutableLiveData<String>().apply { val firstName = MutableLiveData<String>()
value = "Fragment ajouter une attestation" val lastName = MutableLiveData<String>()
val birthday = MutableLiveData<String>()
val birthPlace = MutableLiveData<String>()
val address = MutableLiveData<String>()
val city = MutableLiveData<String>()
val postalCode = MutableLiveData<String>()
val exitDate = MutableLiveData<String>()
val exitHour = MutableLiveData<String>()
@SuppressLint("LongLogTag")
fun onGenerateAttestationClick() {
if (checkAllValue()) {
profileRepository.insertProfile(getProfileFromView())
} else {
Log.e("onGenerateAttestationClick", "Cannot add profile")
} }
val text: LiveData<String> = _text }
init {
setProfileValue()
}
private fun setProfileValue() {
val profile = profileRepository.getProfile()
firstName.value = profile.value?.firstName
lastName.value = profile.value?.lastName
birthday.value = profile.value?.birthday
birthPlace.value = profile.value?.birthPlace
address.value = profile.value?.address
city.value = profile.value?.city
postalCode.value = profile.value?.postalCode
}
private fun getProfileFromView(): Profile {
return Profile(
firstName.value.toString(),
lastName.value.toString(),
birthday.value.toString(),
birthPlace.value.toString(),
address.value.toString(),
city.value.toString(),
postalCode.value.toString()
)
}
private fun checkAllValue(): Boolean {
return !firstName.value.isNullOrEmpty()
&& !lastName.value.isNullOrEmpty()
&& !birthday.value.isNullOrEmpty()
&& !birthPlace.value.isNullOrEmpty()
&& !address.value.isNullOrEmpty()
&& !city.value.isNullOrEmpty()
&& !postalCode.value.isNullOrEmpty()
}
} }

View File

@ -0,0 +1,14 @@
package fr.sanchezm.attestationsCovid19.ui.add
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import fr.sanchezm.attestationsCovid19.data.repository.ProfileRepository
class AddViewModelFactory(private val profileRepository: ProfileRepository) :
ViewModelProvider.NewInstanceFactory() {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return AddViewModel(profileRepository) as T
}
}

View File

@ -0,0 +1,16 @@
package fr.sanchezm.attestationsCovid19.utilities
import android.content.Context
import fr.sanchezm.attestationsCovid19.data.db.MyDatabase
import fr.sanchezm.attestationsCovid19.data.repository.ProfileRepository
import fr.sanchezm.attestationsCovid19.ui.add.AddViewModelFactory
object InjectorUtils {
fun provideAddViewModelFactory(context: Context): AddViewModelFactory {
val profileRepository =
ProfileRepository.getInstance(MyDatabase.invoke(context).profileDao())
return AddViewModelFactory(profileRepository = profileRepository)
}
}

View File

@ -1,9 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="fr.sanchezm.attestationsCovid19.ui.add.AddViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".ui.add.AddFragment"> tools:context=".ui.add.AddFragment">
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
@ -37,7 +46,7 @@
android:textSize="15sp" /> android:textSize="15sp" />
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/firstName" android:id="@+id/first_name"
style="@style/TextInputLayout" style="@style/TextInputLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -46,12 +55,13 @@
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="textPersonName|textCapWords" /> android:inputType="textPersonName|textCapWords"
android:text="@={viewModel.firstName}" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/lastName" android:id="@+id/last_name"
style="@style/TextInputLayout" style="@style/TextInputLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -60,7 +70,8 @@
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="textPersonName|textCapWords" /> android:inputType="textPersonName|textCapWords"
android:text="@{viewModel.lastName}" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
@ -74,12 +85,13 @@
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="date" /> android:inputType="date"
android:text="@{viewModel.birthday}" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/birthPlace" android:id="@+id/birth_place"
style="@style/TextInputLayout" style="@style/TextInputLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -88,7 +100,8 @@
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="text|textCapWords" /> android:inputType="text|textCapWords"
android:text="@{viewModel.birthPlace}" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
@ -102,7 +115,8 @@
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="textPostalAddress|textCapWords" /> android:inputType="textPostalAddress|textCapWords"
android:text="@{viewModel.address}" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
@ -116,12 +130,13 @@
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="text|textCapWords" /> android:inputType="text|textCapWords"
android:text="@{viewModel.city}" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/postalCode" android:id="@+id/postal_code"
style="@style/TextInputLayout" style="@style/TextInputLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -130,12 +145,13 @@
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="text|textCapWords" /> android:inputType="text|textCapWords"
android:text="@{viewModel.postalCode}" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/exitDate" android:id="@+id/exit_date"
style="@style/TextInputLayout" style="@style/TextInputLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -144,12 +160,13 @@
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="date" /> android:inputType="date"
android:text="@{viewModel.exitDate}" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/exitHour" android:id="@+id/exit_hour"
style="@style/TextInputLayout" style="@style/TextInputLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -158,7 +175,8 @@
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="time" /> android:inputType="time"
android:text="@{viewModel.exitHour}" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
@ -171,64 +189,68 @@
android:textSize="18sp" /> android:textSize="18sp" />
<com.google.android.material.checkbox.MaterialCheckBox <com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/reason1" android:id="@+id/reason_1"
style="@style/MaterialCheckBox" style="@style/MaterialCheckBox"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/reason_1" /> android:text="@string/reason_1" />
<com.google.android.material.checkbox.MaterialCheckBox <com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/reason2" android:id="@+id/reason_2"
style="@style/MaterialCheckBox" style="@style/MaterialCheckBox"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/reason_2" /> android:text="@string/reason_2" />
<com.google.android.material.checkbox.MaterialCheckBox <com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/reason3" android:id="@+id/reason_3"
style="@style/MaterialCheckBox" style="@style/MaterialCheckBox"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/reason_3" /> android:text="@string/reason_3" />
<com.google.android.material.checkbox.MaterialCheckBox <com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/reason4" android:id="@+id/reason_4"
style="@style/MaterialCheckBox" style="@style/MaterialCheckBox"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/reason_4" /> android:text="@string/reason_4" />
<com.google.android.material.checkbox.MaterialCheckBox <com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/reason5" android:id="@+id/reason_5"
style="@style/MaterialCheckBox" style="@style/MaterialCheckBox"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/reason_5" /> android:text="@string/reason_5" />
<com.google.android.material.checkbox.MaterialCheckBox <com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/reason6" android:id="@+id/reason_6"
style="@style/MaterialCheckBox" style="@style/MaterialCheckBox"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/reason_6" /> android:text="@string/reason_6" />
<com.google.android.material.checkbox.MaterialCheckBox <com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/reason7" android:id="@+id/reason_7"
style="@style/MaterialCheckBox" style="@style/MaterialCheckBox"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/reason_7" /> android:text="@string/reason_7" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/containedButton" android:id="@+id/generate_attestation"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="20dp" android:layout_marginTop="20dp"
android:enabled="false" android:onClick="@{() -> viewModel.onGenerateAttestationClick()}"
android:text="@string/generate_attestation_button" /> android:text="@string/generate_attestation_button" />
<!-- android:enabled="false"-->
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@ -1,2 +1,2 @@
rootProject.name='My Application' rootProject.name='Attestation Dérogatoire de déplacement COVID-19'
include ':app' include ':app'