diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..603b140 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..88ea3aa --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,122 @@ + + + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+ + +
+
\ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..5cd135a --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..37a7509 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..2874e5c --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,67 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' +apply plugin: 'kotlin-kapt' +apply plugin: 'androidx.navigation.safeargs' + +android { + compileSdkVersion 29 + buildToolsVersion "29.0.3" + + defaultConfig { + applicationId "com.taymath.tutortoolkit" + minSdkVersion 19 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + vectorDrawables.useSupportLibrary = true + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + + // Enables data binding. + dataBinding { + enabled = true + } + +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$version_kotlin" + + + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.core:core-ktx:1.2.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + + // Support libraries + implementation "androidx.appcompat:appcompat:$version_appcompat" + implementation "androidx.fragment:fragment:$version_fragment" + implementation "androidx.constraintlayout:constraintlayout:$version_constraint_layout" + + // Room and Lifecycle dependencies + implementation "androidx.room:room-runtime:$version_room" + kapt "androidx.room:room-compiler:$version_room" + implementation "androidx.lifecycle:lifecycle-extensions:$version_lifecycle_extensions" + + // Navigation + implementation 'androidx.navigation:navigation-fragment-ktx:2.2.1' + implementation 'androidx.navigation:navigation-ui-ktx:2.2.1' + + // Coroutines + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version_coroutine" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version_coroutine" + + // Testing + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test.ext:junit:1.1.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/src/androidTest/java/com/taymath/tutortoolkit/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/taymath/tutortoolkit/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..4542276 --- /dev/null +++ b/app/src/androidTest/java/com/taymath/tutortoolkit/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.taymath.tutortoolkit + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.taymath.tutortoolkit", appContext.packageName) + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..e000ff3 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000..7817754 Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ diff --git a/app/src/main/java/com/taymath/tutortoolkit/MainActivity.kt b/app/src/main/java/com/taymath/tutortoolkit/MainActivity.kt new file mode 100644 index 0000000..1d463f7 --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/MainActivity.kt @@ -0,0 +1,17 @@ +package com.taymath.tutortoolkit + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle + +/** + * This main activity is just a container for our fragments, + * where the real action is. + */ +class MainActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.activity_main) + } +} diff --git a/app/src/main/java/com/taymath/tutortoolkit/addgrade/AddGradeFragment.kt b/app/src/main/java/com/taymath/tutortoolkit/addgrade/AddGradeFragment.kt new file mode 100644 index 0000000..c972d82 --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/addgrade/AddGradeFragment.kt @@ -0,0 +1,68 @@ +package com.taymath.tutortoolkit.addgrade + +import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.EditText +import androidx.databinding.DataBindingUtil +import androidx.fragment.app.Fragment +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import androidx.navigation.fragment.findNavController +import com.taymath.tutortoolkit.R +import com.taymath.tutortoolkit.addstudent.AddStudentFragmentDirections +import com.taymath.tutortoolkit.databinding.FragmentAddGradeBindingImpl +import com.taymath.tutortoolkit.databinding.FragmentAddStudentBindingImpl +import com.taymath.tutortoolkit.studentdatabase.StudentDatabase +import kotlinx.android.synthetic.main.fragment_add_grade.* + +class AddGradeFragment : Fragment() { + + /** + * Called when the Fragment is ready to display content to the screen. + * + * This function uses DataBindingUtil to inflate R.layout.fragment_add_grade. + */ + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + + // Get a reference to the binding object and inflate the fragment views. + val binding: FragmentAddGradeBindingImpl = DataBindingUtil.inflate( + inflater, R.layout.fragment_add_grade, container, false) + + // Get reference to application + val application = requireNotNull(this.activity).application + + // Get a reference to the DAO + val dataSource = StudentDatabase.getInstance(application).studentDatabaseDao + + // Grab our arguments from previous fragment + val arguments = AddGradeFragmentArgs.fromBundle(arguments!!) + + // Create instance of viewModelFactory using DAO and application + val viewModelFactory = AddGradeViewModelFactory(arguments.studentId, dataSource) + + // Get reference to viewModel + val addGradeViewModel = + ViewModelProviders.of( + this, viewModelFactory).get(AddGradeViewModel::class.java) + + // Add view model to our binding + binding.addGradeViewModel = addGradeViewModel + + binding.setLifecycleOwner(this) + + // Listen for when we navigate to student list + addGradeViewModel.navigateToStudentList.observe(viewLifecycleOwner, Observer { + if (it == true) { // Observed state is true. + this.findNavController().navigate( + AddGradeFragmentDirections.actionAddGradeFragmentToStudentListFragment()) + addGradeViewModel.doneNavigating() + } + }) + return binding.root + } +} \ No newline at end of file diff --git a/app/src/main/java/com/taymath/tutortoolkit/addgrade/AddGradeViewModel.kt b/app/src/main/java/com/taymath/tutortoolkit/addgrade/AddGradeViewModel.kt new file mode 100644 index 0000000..07c3857 --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/addgrade/AddGradeViewModel.kt @@ -0,0 +1,79 @@ +package com.taymath.tutortoolkit.addgrade + +import android.content.Context +import androidx.lifecycle.LiveData +import androidx.lifecycle.MediatorLiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.taymath.tutortoolkit.studentdatabase.Grade +import com.taymath.tutortoolkit.studentdatabase.Student +import com.taymath.tutortoolkit.studentdatabase.StudentDatabaseDao +import kotlinx.coroutines.* + +class AddGradeViewModel( + var student_id: Long, + val database: StudentDatabaseDao +) : ViewModel() { + + // Initialize viewModelJob + private val viewModelJob = Job() + + // Initialize uiScope + private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob) + + // Setup Livdata to signal when to navigate to studentList + private val _navigateToStudentList = MutableLiveData() + val navigateToStudentList: LiveData + get() = _navigateToStudentList + + val gradeFloatText = MutableLiveData() + + // Initialize mediatorLiveData containing Student Class + private val student = MediatorLiveData() + + fun getStudent() = student + + // Add Student from database to our mediatorLiveData + init { + student.addSource(database.getStudentWithId(student_id), student::setValue) + } + +// fun validateForm(): Boolean { +// return gradeFloatText.value!!.isNotEmpty() +// } + + + // Initialize Grade and add to grade_table + fun onAddGrade( + gradeString: String, + gradeFloat: Float, + noteString: String, + studentNameString: String + ) { + uiScope.launch { + withContext(Dispatchers.IO) { + val newGrade = Grade() + newGrade.studentNameString = studentNameString + newGrade.gradeString = gradeString + newGrade.gradeFloat = gradeFloat + newGrade.noteString = noteString + newGrade.studentIdLong = student_id + insert(newGrade) + } + } + _navigateToStudentList.value = true + } + + override fun onCleared() { + super.onCleared() + viewModelJob.cancel() + } + + fun doneNavigating() { + _navigateToStudentList.value = false + } + + private fun insert(grade: Grade) { + database.insertGrade(grade) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/taymath/tutortoolkit/addgrade/AddGradeViewModelFactory.kt b/app/src/main/java/com/taymath/tutortoolkit/addgrade/AddGradeViewModelFactory.kt new file mode 100644 index 0000000..8f786dd --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/addgrade/AddGradeViewModelFactory.kt @@ -0,0 +1,20 @@ +package com.taymath.tutortoolkit.addgrade + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.taymath.tutortoolkit.studentdatabase.Student +import com.taymath.tutortoolkit.studentdatabase.StudentDatabaseDao + +class AddGradeViewModelFactory( + private val student_id: Long, + private val dataSource: StudentDatabaseDao +) : ViewModelProvider.Factory { + + @Suppress("unchecked_cast") + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(AddGradeViewModel::class.java)) { + return AddGradeViewModel(student_id, dataSource) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } +} diff --git a/app/src/main/java/com/taymath/tutortoolkit/addstudent/AddStudentFragment.kt b/app/src/main/java/com/taymath/tutortoolkit/addstudent/AddStudentFragment.kt new file mode 100644 index 0000000..3532589 --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/addstudent/AddStudentFragment.kt @@ -0,0 +1,59 @@ +package com.taymath.tutortoolkit.addstudent + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import androidx.fragment.app.Fragment +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import androidx.navigation.fragment.findNavController +import com.taymath.tutortoolkit.R +import com.taymath.tutortoolkit.databinding.FragmentAddStudentBindingImpl +import com.taymath.tutortoolkit.studentdatabase.StudentDatabase + +class AddStudentFragment : Fragment() { + + /** + * Called when the Fragment is ready to display content to the screen. + * + * This function uses DataBindingUtil to inflate R.layout.fragment_add_student. + */ + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + + // Get a reference to the binding object and inflate the fragment views. + val binding: FragmentAddStudentBindingImpl = DataBindingUtil.inflate( + inflater, R.layout.fragment_add_student, container, false) + + // Get reference to application + val application = requireNotNull(this.activity).application + + // Get a reference to the DAO + val dataSource = StudentDatabase.getInstance(application).studentDatabaseDao + + // Create instance of viewModelFactory using DAO and application + val viewModelFactory = AddStudentViewModelFactory(dataSource) + + // Get reference to viewModel + val addStudentViewModel = + ViewModelProviders.of( + this, viewModelFactory).get(AddStudentViewModel::class.java) + + // Add our viewModel to binding + binding.addStudentViewModel = addStudentViewModel + + binding.setLifecycleOwner(this) + + // Listen for when we navigate to student list + addStudentViewModel.navigateToStudentList.observe(viewLifecycleOwner, Observer { + if (it == true) { // Observed state is true. + this.findNavController().navigate( + AddStudentFragmentDirections.actionAddStudentFragmentToStudentListFragment()) + addStudentViewModel.doneNavigating() + } + }) + return binding.root + } +} \ No newline at end of file diff --git a/app/src/main/java/com/taymath/tutortoolkit/addstudent/AddStudentViewModel.kt b/app/src/main/java/com/taymath/tutortoolkit/addstudent/AddStudentViewModel.kt new file mode 100644 index 0000000..bc40714 --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/addstudent/AddStudentViewModel.kt @@ -0,0 +1,67 @@ +package com.taymath.tutortoolkit.addstudent + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.taymath.tutortoolkit.studentdatabase.Student +import com.taymath.tutortoolkit.studentdatabase.StudentDatabaseDao +import kotlinx.coroutines.* + +class AddStudentViewModel( + val database: StudentDatabaseDao +) : ViewModel() { + + // Initialize viewModelJob + private val viewModelJob = Job() + + // Initialize uiScope + private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob) + + // Setup Livdata to signal when to navigate to studentList + private val _navigateToStudentList = MutableLiveData() + val navigateToStudentList: LiveData + get() = _navigateToStudentList + + // Initialize Student and add to student_table + fun onAddStudent(studentNameString : String, subjectString: String, + gradeLevelString: String, addressString: String, emailString: String) { + uiScope.launch { + withContext(Dispatchers.IO) { + val newStudent = Student() + newStudent.studentNameString = studentNameString + newStudent.subjectString = subjectString + newStudent.emailString = emailString + newStudent.gradeLevelString = gradeLevelString + newStudent.addressString = addressString + insert(newStudent) + } + } + _navigateToStudentList.value = true + } + + override fun onCleared() { + super.onCleared() + viewModelJob.cancel() + } + + fun doneNavigating() { + _navigateToStudentList.value = null + } + + private suspend fun insert(student: Student) { + withContext(Dispatchers.IO) { + database.insertStudent(student) + } + } + +// fun onSetSleepQuality(quality: Int) { +// uiScope.launch { +// withContext(Dispatchers.IO) { +// val tonight = database.get(sleepNightKey) ?: return@withContext +// tonight.sleepQuality = quality +// database.update(tonight) +// } +// _navigateToSleepTracker.value = true +// } +// } +} \ No newline at end of file diff --git a/app/src/main/java/com/taymath/tutortoolkit/addstudent/AddStudentViewModelFactory.kt b/app/src/main/java/com/taymath/tutortoolkit/addstudent/AddStudentViewModelFactory.kt new file mode 100644 index 0000000..58c5206 --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/addstudent/AddStudentViewModelFactory.kt @@ -0,0 +1,18 @@ +package com.taymath.tutortoolkit.addstudent + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.taymath.tutortoolkit.studentdatabase.StudentDatabaseDao + +class AddStudentViewModelFactory( + private val dataSource: StudentDatabaseDao +) : ViewModelProvider.Factory { + + @Suppress("unchecked_cast") + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(AddStudentViewModel::class.java)) { + return AddStudentViewModel(dataSource) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } +} diff --git a/app/src/main/java/com/taymath/tutortoolkit/chooseicon/ChooseIconFragment.kt b/app/src/main/java/com/taymath/tutortoolkit/chooseicon/ChooseIconFragment.kt new file mode 100644 index 0000000..f192317 --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/chooseicon/ChooseIconFragment.kt @@ -0,0 +1,59 @@ +package com.taymath.tutortoolkit.chooseicon + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProviders +import androidx.navigation.fragment.findNavController +import com.taymath.tutortoolkit.R +import com.taymath.tutortoolkit.databinding.FragmentChooseIconBinding +import com.taymath.tutortoolkit.studentdatabase.StudentDatabase + +class ChooseIconFragment : Fragment() { + + /** + * Called when the Fragment is ready to display content to the screen. + * + * This function uses DataBindingUtil to inflate R.layout.fragment_sleep_quality. + */ + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + + // Get a reference to the binding object and inflate the fragment views. + val binding: FragmentChooseIconBinding = DataBindingUtil.inflate( + inflater, R.layout.fragment_choose_icon, container, false) + + val application = requireNotNull(this.activity).application + +// val arguments = SleepQualityFragmentArgs.fromBundle(arguments!!) + + // Create an instance of the ViewModel Factory. + val dataSource = StudentDatabase.getInstance(application).studentDatabaseDao + val viewModelFactory = ChooseIconViewModelFactory(dataSource) + + // Get a reference to the ViewModel associated with this fragment. + val chooseIconViewModel = + ViewModelProviders.of( + this, viewModelFactory).get(ChooseIconViewModel::class.java) + + // To use the View Model with data binding, you have to explicitly + // give the binding object a reference to it. + binding.chooseIconViewModel = chooseIconViewModel + + // Add an Observer to the state variable for Navigating when a Quality icon is tapped. +// chooseIconViewModel.navigateToSleepTracker.observe(this, Observer { +// if (it == true) { // Observed state is true. +// this.findNavController().navigate( +// SleepQualityFragmentDirections.actionSleepQualityFragmentToSleepTrackerFragment()) +// // Reset state to make sure we only navigate once, even if the device +// // has a configuration change. +// sleepQualityViewModel.doneNavigating() +// } +// }) + + return binding.root + } +} \ No newline at end of file diff --git a/app/src/main/java/com/taymath/tutortoolkit/chooseicon/ChooseIconViewModel.kt b/app/src/main/java/com/taymath/tutortoolkit/chooseicon/ChooseIconViewModel.kt new file mode 100644 index 0000000..83404eb --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/chooseicon/ChooseIconViewModel.kt @@ -0,0 +1,81 @@ +package com.taymath.tutortoolkit.chooseicon + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.taymath.tutortoolkit.studentdatabase.StudentDatabaseDao +import kotlinx.coroutines.* + +class ChooseIconViewModel( + val database: StudentDatabaseDao) : ViewModel() { + + /** Coroutine setup variables */ + + /** + * viewModelJob allows us to cancel all coroutines started by this ViewModel. + */ + private val viewModelJob = Job() + + /** + * A [CoroutineScope] keeps track of all coroutines started by this ViewModel. + * + * Because we pass it [viewModelJob], any coroutine started in this scope can be cancelled + * by calling `viewModelJob.cancel()` + * + * By default, all coroutines started in uiScope will launch in [Dispatchers.Main] which is + * the main thread on Android. This is a sensible default because most coroutines started by + * a [ViewModel] update the UI after performing some processing. + */ + private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob) + + /** + * Variable that tells the fragment whether it should navigate to [SleepTrackerFragment]. + * + * This is `private` because we don't want to expose the ability to set [MutableLiveData] to + * the [Fragment] + */ + private val _navigateToSleepTracker = MutableLiveData() + + /** + * When true immediately navigate back to the [SleepTrackerFragment] + */ + val navigateToSleepTracker: LiveData + get() = _navigateToSleepTracker + + /** + * Cancels all coroutines when the ViewModel is cleared, to cleanup any pending work. + * + * onCleared() gets called when the ViewModel is destroyed. + */ + override fun onCleared() { + super.onCleared() + viewModelJob.cancel() + } + + /** + * Call this immediately after navigating to [SleepTrackerFragment] + */ + fun doneNavigating() { + _navigateToSleepTracker.value = null + } + + /** + * Sets the sleep quality and updates the database. + * + * Then navigates back to the SleepTrackerFragment. + */ +// fun onSetSleepQuality(quality: Int) { +// uiScope.launch { +// // IO is a thread pool for running operations that access the disk, such as +// // our Room database. +// withContext(Dispatchers.IO) { +// val tonight = database.get(sleepNightKey) ?: return@withContext +// tonight.sleepQuality = quality +// database.update(tonight) +// } +// +// // Setting this state variable to true will alert the observer and trigger navigation. +// _navigateToSleepTracker.value = true +// } +// } +} \ No newline at end of file diff --git a/app/src/main/java/com/taymath/tutortoolkit/chooseicon/ChooseIconViewModelFactory.kt b/app/src/main/java/com/taymath/tutortoolkit/chooseicon/ChooseIconViewModelFactory.kt new file mode 100644 index 0000000..cffed31 --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/chooseicon/ChooseIconViewModelFactory.kt @@ -0,0 +1,16 @@ +package com.taymath.tutortoolkit.chooseicon + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.taymath.tutortoolkit.studentdatabase.StudentDatabaseDao + +class ChooseIconViewModelFactory( + private val dataSource: StudentDatabaseDao) : ViewModelProvider.Factory { + @Suppress("unchecked_cast") + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(ChooseIconViewModel::class.java)) { + return ChooseIconViewModel(dataSource) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/taymath/tutortoolkit/studentdatabase/Grade.kt b/app/src/main/java/com/taymath/tutortoolkit/studentdatabase/Grade.kt new file mode 100644 index 0000000..4f81180 --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/studentdatabase/Grade.kt @@ -0,0 +1,30 @@ +package com.taymath.tutortoolkit.studentdatabase + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "grade_table") +data class Grade ( + + @PrimaryKey(autoGenerate = true) + var gradeId: Long = 0L, + + @ColumnInfo(name="grade_string") + var gradeString: String = "BLANK Grade", + + @ColumnInfo(name = "student_name_string") + var studentNameString: String = "CHECK_GRADE.KT", + + @ColumnInfo(name = "grade_float") + var gradeFloat: Float = 0F, + + @ColumnInfo(name = "time_milli") + val startTimeMilli: Long = System.currentTimeMillis(), + + @ColumnInfo(name = "note_string") + var noteString: String = "CHECK_GRADE.KT", + + @ColumnInfo(name= "student_id_long") + var studentIdLong: Long = 0L +) \ No newline at end of file diff --git a/app/src/main/java/com/taymath/tutortoolkit/studentdatabase/Student.kt b/app/src/main/java/com/taymath/tutortoolkit/studentdatabase/Student.kt new file mode 100644 index 0000000..ff390c5 --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/studentdatabase/Student.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.taymath.tutortoolkit.studentdatabase + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "student_table") +data class Student ( + + @PrimaryKey(autoGenerate = true) + var studentId: Long = 0L, + + @ColumnInfo(name="student_name") + var studentNameString: String = "BLANK STUDENT", + + @ColumnInfo(name = "todo_string") + var todoString: String = "CHECK_Student.KT", + + @ColumnInfo(name = "subject") + var subjectString: String = "CHECK_Student.KT", + + @ColumnInfo(name = "grade_level") + var gradeLevelString: String = "CHECK_Student.KT", + + @ColumnInfo(name = "address") + var addressString: String = "CHECK_Student.KT", + + @ColumnInfo(name = "email") + var emailString: String = "CHECK_Student.KT" + +) diff --git a/app/src/main/java/com/taymath/tutortoolkit/studentdatabase/StudentDatabase.kt b/app/src/main/java/com/taymath/tutortoolkit/studentdatabase/StudentDatabase.kt new file mode 100644 index 0000000..a97f608 --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/studentdatabase/StudentDatabase.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.taymath.tutortoolkit.studentdatabase + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase + +@Database(entities = [Student::class, Grade::class], version = 1, exportSchema = false) +abstract class StudentDatabase : RoomDatabase() { + + abstract val studentDatabaseDao: StudentDatabaseDao + + companion object { + @Volatile + private var INSTANCE: StudentDatabase? = null + + fun getInstance(context: Context): StudentDatabase { + synchronized(this) { + var instance = INSTANCE + + if (instance == null) { + instance = Room.databaseBuilder( + context.applicationContext, + StudentDatabase::class.java, + "student_database" + ).fallbackToDestructiveMigration().build() + } + + return instance + } + } + } +} diff --git a/app/src/main/java/com/taymath/tutortoolkit/studentdatabase/StudentDatabaseDao.kt b/app/src/main/java/com/taymath/tutortoolkit/studentdatabase/StudentDatabaseDao.kt new file mode 100644 index 0000000..e191532 --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/studentdatabase/StudentDatabaseDao.kt @@ -0,0 +1,87 @@ +/* + * Copyright 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.taymath.tutortoolkit.studentdatabase + +import androidx.lifecycle.LiveData +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Update + +@Dao +interface StudentDatabaseDao { + + // Student Table Dao + @Insert + fun insertStudent(student: Student) + + @Update + fun updateStudent(student: Student) + + @Query("DELETE FROM student_table WHERE studentId = :studentId") + fun deleteStudent(studentId: Long) + + @Query("DELETE FROM student_table") + fun clearStudentTable() + + @Query("SELECT * FROM student_table ORDER BY studentId DESC LIMIT 1") + fun getStudent(): Student? + + @Query("SELECT * FROM student_table ORDER BY studentId DESC") + fun getAllStudents(): LiveData> + + @Query("SELECT * from student_table WHERE studentId = :key") + fun getStudentWithId(key: Long): LiveData + + @Query("SELECT * FROM student_table WHERE studentId = :studentId") + fun getStudentClassWithId(studentId: Long): Student + + @Query("SELECT * from student_table WHERE student_name = :studentName") + fun getStudentWithName(studentName: String): LiveData + + + + // Grade Table Dao + @Insert + fun insertGrade(grade: Grade) + + @Update + fun updateGrade(grade: Grade) + + @Query("DELETE FROM grade_table WHERE student_id_long = :studentId") + fun deleteGradeTable(studentId: Long) + + @Query("DELETE FROM grade_table") + fun clearGradeTable() + + @Query("SELECT * FROM grade_table ORDER BY gradeId DESC LIMIT 1") + fun getGrade(): Grade? + + @Query("SELECT * FROM grade_table ORDER BY student_name_string DESC") + fun getAllGrades(): LiveData> + + @Query("SELECT * FROM grade_table WHERE student_name_string = :studentName") + fun getGradesForStudent(studentName: String): LiveData> + + @Query("SELECT * from grade_table WHERE gradeId = :key") + fun getGradeWithId(key: Long): LiveData + + @Query("SELECT * from grade_table WHERE student_id_long = :studentId") + fun getGradesWithStudentId(studentId: Long): LiveData> + +} + diff --git a/app/src/main/java/com/taymath/tutortoolkit/studentdetail/GradeAdapter.kt b/app/src/main/java/com/taymath/tutortoolkit/studentdetail/GradeAdapter.kt new file mode 100644 index 0000000..ba0f8cb --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/studentdetail/GradeAdapter.kt @@ -0,0 +1,66 @@ +package com.taymath.tutortoolkit.studentdetail + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.taymath.tutortoolkit.databinding.ListItemGradeBinding +import com.taymath.tutortoolkit.studentdatabase.Grade + +class GradeAdapter : ListAdapter(GradeDiffCallback()) { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder.from(parent) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.bind(getItem(position)) + } + + class ViewHolder private constructor(val binding: ListItemGradeBinding) + : RecyclerView.ViewHolder(binding.root) { + + fun bind(item: Grade) { + binding.grade = item + binding.executePendingBindings() + } + + companion object { + fun from(parent: ViewGroup): ViewHolder { + val layoutInflater = LayoutInflater.from(parent.context) + val binding = ListItemGradeBinding.inflate(layoutInflater, parent, false) + return ViewHolder(binding) + } + } + } + + class GradeDiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: Grade, newItem: Grade): Boolean { + return oldItem.gradeId == newItem.gradeId + } + + override fun areContentsTheSame(oldItem: Grade, newItem: Grade): Boolean { + return oldItem == newItem + } + } +} + +/** + * Callback for calculating the diff between two non-null items in a list. + * + * Used by ListAdapter to calculate the minumum number of changes between and old list and a new + * list that's been passed to `submitList`. + */ + + +class GradeDiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: Grade, newItem: Grade): Boolean { + return oldItem.gradeId == newItem.gradeId + } + + override fun areContentsTheSame(oldItem: Grade, newItem: Grade): Boolean { + return oldItem == newItem + } +} \ No newline at end of file diff --git a/app/src/main/java/com/taymath/tutortoolkit/studentdetail/GradeBindingUtils.kt b/app/src/main/java/com/taymath/tutortoolkit/studentdetail/GradeBindingUtils.kt new file mode 100644 index 0000000..4b4a9f4 --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/studentdetail/GradeBindingUtils.kt @@ -0,0 +1,26 @@ +package com.taymath.tutortoolkit.studentdetail + +import android.widget.TextView +import androidx.databinding.BindingAdapter +import com.taymath.tutortoolkit.studentdatabase.Grade + +@BindingAdapter("gradeFloat") +fun TextView.setGradeFloat(item: Grade?){ + item?.let { + text = item.gradeFloat.toString() + } +} + +@BindingAdapter("gradeString") +fun TextView.setSubjectText(item: Grade?){ + item?.let { + text =item.gradeString + } +} + +@BindingAdapter("noteString") +fun TextView.setStudentNameText(item: Grade?){ + item?.let { + text =item.noteString + } +} diff --git a/app/src/main/java/com/taymath/tutortoolkit/studentdetail/StudentDetailFragment.kt b/app/src/main/java/com/taymath/tutortoolkit/studentdetail/StudentDetailFragment.kt new file mode 100644 index 0000000..9de61f2 --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/studentdetail/StudentDetailFragment.kt @@ -0,0 +1,79 @@ +package com.taymath.tutortoolkit.studentdetail + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import androidx.fragment.app.Fragment +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import androidx.navigation.fragment.findNavController +import com.taymath.tutortoolkit.R +import com.taymath.tutortoolkit.databinding.FragmentStudentDetailBinding +import com.taymath.tutortoolkit.studentdatabase.StudentDatabase +import com.taymath.tutortoolkit.studentlist.StudentListFragmentDirections +import com.taymath.tutortoolkit.studentlist.StudentListener + +class StudentDetailFragment: Fragment() { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + + val binding:FragmentStudentDetailBinding = DataBindingUtil.inflate( + inflater, R.layout.fragment_student_detail, container,false + ) + + val application = requireNotNull(this.activity).application + val arguments = StudentDetailFragmentArgs.fromBundle(arguments!!) + + // Create an instance of the ViewModel Factory. + val dataSource = StudentDatabase.getInstance(application).studentDatabaseDao + val viewModelFactory = StudentDetailViewModelFactory(dataSource, arguments.studentId) + + // Get a reference to the ViewModel associated with this fragment. + val studentDetailViewModel = + ViewModelProviders.of( + this, viewModelFactory).get(StudentDetailViewModel::class.java) + + // To use the View Model with data binding, you have to explicitly + // give the binding object a reference to it. + binding.studentDetailViewModel = studentDetailViewModel + + binding.setLifecycleOwner(this) + + // Add an Observer to the state variable for Navigating when Add Grade button is clicked. + studentDetailViewModel.navigateToAddGrade.observe(viewLifecycleOwner, Observer { + if (it == true) { // Observed state is true. + this.findNavController().navigate( + StudentDetailFragmentDirections.actionStudentDetailFragmentToAddGradeFragment(arguments.studentId)) + // Reset state to make sure we only navigate once, even if the device + // has a configuration change. + studentDetailViewModel.doneNavigating() + } + }) + + // Add an Observer to the state variable for Navigating when Add Grade button is clicked. + studentDetailViewModel.navigateToStudentList.observe(viewLifecycleOwner, Observer { + if (it == true) { // Observed state is true. + this.findNavController().navigate( + StudentDetailFragmentDirections.actionStudentDetailFragmentToStudentListFragment()) + // Reset state to make sure we only navigate once, even if the device + // has a configuration change. + studentDetailViewModel.doneNavigating() + } + }) + + // Initialize adapter and add to gradeList + val adapter = GradeAdapter() + binding.gradeList.adapter = adapter + + // If we have a list of grades, send it to the adapter + studentDetailViewModel.grades.observe(viewLifecycleOwner, Observer { + it?.let { + adapter.submitList(it) + } + }) + + return binding.root + } +} \ No newline at end of file diff --git a/app/src/main/java/com/taymath/tutortoolkit/studentdetail/StudentDetailViewModel.kt b/app/src/main/java/com/taymath/tutortoolkit/studentdetail/StudentDetailViewModel.kt new file mode 100644 index 0000000..93d27c1 --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/studentdetail/StudentDetailViewModel.kt @@ -0,0 +1,97 @@ +package com.taymath.tutortoolkit.studentdetail + +import androidx.lifecycle.* +import com.taymath.tutortoolkit.studentdatabase.Student +import com.taymath.tutortoolkit.studentdatabase.StudentDatabaseDao +import kotlinx.coroutines.* + +class StudentDetailViewModel( + val studentId: Long, + dataSource: StudentDatabaseDao +): ViewModel() { + + /** + * Hold a reference to StudentDatabase via its StudentDatabaseDao. + */ + val database = dataSource + + // Initialize mediatorLiveData with student datatype + private val student = MediatorLiveData() + fun getStudent() = student + + // Grab grades from grades_table with StudentId + val grades = database.getGradesWithStudentId(studentId) + + // add student from database as source for student + init { + student.addSource(database.getStudentWithId(studentId), student::setValue) + } + + // Initialize viewModelJob + private val viewModelJob = Job() + + // Initialize uiScope + private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob) + + // Setup Livedata to signal when to navigate to AddGrade + private val _navigateToAddGrade = MutableLiveData() + val navigateToAddGrade: LiveData + get() = _navigateToAddGrade + + // Setup Livedata to signal when to navigate to studentList + private val _navigateToStudentList = MutableLiveData() + val navigateToStudentList: LiveData + get() = _navigateToStudentList + + // Setup Livedata to tell whethere the clear button should be visible + // Based on the contents of our grades list + val clearButtonVisible = Transformations.map(grades) { + it?.isNotEmpty() + } + + fun onNavigateToStudentList() { + _navigateToStudentList.value = true + } + + fun onNavigateToAddGrade() { + _navigateToAddGrade.value = true + } + + override fun onCleared() { + super.onCleared() + viewModelJob.cancel() + } + + fun doneNavigating() { + _navigateToAddGrade.value = null + _navigateToStudentList.value = null + } + + fun onClear() { + uiScope.launch { + // Clear the database table. + clearGrades(studentId) + } + } + + private suspend fun clearGrades(studentId: Long) { + withContext(Dispatchers.IO) { + database.deleteGradeTable(studentId) + } + } + + private suspend fun deleteStudent(studentId: Long) { + withContext(Dispatchers.IO) { + database.deleteGradeTable(studentId) + database.deleteStudent(studentId) + } + } + + fun onDeleteStudent() { + uiScope.launch { + deleteStudent(studentId) + } + _navigateToStudentList.value = true + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/taymath/tutortoolkit/studentdetail/StudentDetailViewModelFactory.kt b/app/src/main/java/com/taymath/tutortoolkit/studentdetail/StudentDetailViewModelFactory.kt new file mode 100644 index 0000000..2ac0467 --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/studentdetail/StudentDetailViewModelFactory.kt @@ -0,0 +1,19 @@ +package com.taymath.tutortoolkit.studentdetail + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.taymath.tutortoolkit.studentdatabase.Student +import com.taymath.tutortoolkit.studentdatabase.StudentDatabaseDao + +class StudentDetailViewModelFactory( + private val dataSource: StudentDatabaseDao, + private val studentId: Long +) : ViewModelProvider.Factory { + @Suppress("unchecked_cast") + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(StudentDetailViewModel::class.java)) { + return StudentDetailViewModel(studentId, dataSource) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/taymath/tutortoolkit/studentlist/StudentAdapter.kt b/app/src/main/java/com/taymath/tutortoolkit/studentlist/StudentAdapter.kt new file mode 100644 index 0000000..40eafef --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/studentlist/StudentAdapter.kt @@ -0,0 +1,55 @@ +package com.taymath.tutortoolkit.studentlist + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.taymath.tutortoolkit.databinding.ListItemStudentBinding +import com.taymath.tutortoolkit.studentdatabase.Student + +class StudentAdapter(val clickListener: StudentListener) : ListAdapter(StudentDiffCallback()) { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder.from(parent) + } + + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.bind(clickListener, getItem(position)!!) + } + + + class ViewHolder private constructor(val binding: ListItemStudentBinding): RecyclerView.ViewHolder(binding.root) { + + fun bind(clickListener: StudentListener, item: Student) { + binding.student = item + binding.clickListener = clickListener + binding.executePendingBindings() + } + + companion object { + fun from(parent: ViewGroup): ViewHolder { + val layoutInflater = LayoutInflater.from(parent.context) + val binding = ListItemStudentBinding.inflate(layoutInflater, parent, false) + return ViewHolder(binding) + } + } + + } + + class StudentDiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: Student, newItem: Student): Boolean { + return oldItem.studentId == newItem.studentId + } + + override fun areContentsTheSame(oldItem: Student, newItem: Student): Boolean { + return oldItem == newItem + } + + } +} + +class StudentListener(val clickListener: (studentId: Long) -> Unit) { + fun onClick(student: Student) = clickListener(student.studentId) +} \ No newline at end of file diff --git a/app/src/main/java/com/taymath/tutortoolkit/studentlist/StudentBindingUtils.kt b/app/src/main/java/com/taymath/tutortoolkit/studentlist/StudentBindingUtils.kt new file mode 100644 index 0000000..d05eb10 --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/studentlist/StudentBindingUtils.kt @@ -0,0 +1,35 @@ +package com.taymath.tutortoolkit.studentlist + +import android.widget.ImageView +import android.widget.TextView +import androidx.databinding.BindingAdapter +import com.taymath.tutortoolkit.R +import com.taymath.tutortoolkit.studentdatabase.Student + +@BindingAdapter("studentImage") +fun ImageView.setStudentImage(item: Student?){ + item?.let { + setImageResource(R.drawable.ic_item_test) + } +} + +@BindingAdapter("subjectText") +fun TextView.setSubjectText(item: Student?){ + item?.let { + text =item.subjectString + } +} + +@BindingAdapter("studentNameText") +fun TextView.setStudentNameText(item: Student?){ + item?.let { + text =item.studentNameString + } +} + +@BindingAdapter("gradeLevelText") +fun TextView.setGradeLevelText(item: Student?){ + item?.let { + text =item.gradeLevelString + } +} diff --git a/app/src/main/java/com/taymath/tutortoolkit/studentlist/StudentListFragment.kt b/app/src/main/java/com/taymath/tutortoolkit/studentlist/StudentListFragment.kt new file mode 100644 index 0000000..c0efa9f --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/studentlist/StudentListFragment.kt @@ -0,0 +1,88 @@ +package com.taymath.tutortoolkit.studentlist + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import androidx.fragment.app.Fragment +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import androidx.navigation.fragment.findNavController +import com.taymath.tutortoolkit.R +import com.taymath.tutortoolkit.databinding.FragmentStudentListBinding +import com.taymath.tutortoolkit.studentdatabase.StudentDatabase +import kotlinx.android.synthetic.main.fragment_add_student.* + +/** + * A fragment with buttons to record start and end times for sleep, which are saved in + * a database. Cumulative data is displayed in a simple scrollable TextView. + * (Because we have not learned about RecyclerView yet.) + */ +class StudentListFragment : Fragment() { + + /** + * Called when the Fragment is ready to display content to the screen. + * + * This function uses DataBindingUtil to inflate R.layout.fragment_get_todo. + */ + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + + // Get a reference to the binding object and inflate the fragment views. + val binding: FragmentStudentListBinding = DataBindingUtil.inflate( + inflater, R.layout.fragment_student_list, container, false) + + // Get reference to application + val application = requireNotNull(this.activity).application + + // Get a reference to the DAO + val dataSource = StudentDatabase.getInstance(application).studentDatabaseDao + + // Create instance of viewModelFactory using DAO and application + val viewModelFactory = StudentListViewModelFactory(dataSource, application) + + // Get reference to viewModel + val studentListViewModel = + ViewModelProviders.of( + this, viewModelFactory).get(StudentListViewModel::class.java) + + // Add ViewModel to our binding + binding.studentListViewModel = studentListViewModel + + binding.setLifecycleOwner(this) + + // Add an Observer to the state variable for Navigating when Add Student button is clicked. + studentListViewModel.navigateToAddStudent.observe(viewLifecycleOwner, Observer { + if (it == true) { // Observed state is true. + this.findNavController().navigate( + StudentListFragmentDirections.actionStudentListFragmentToAddStudentFragment()) + studentListViewModel.onAddStudentNavigated() + } + }) + + // Add an Observer to the state variable for Navigating when student icon is clicked. + studentListViewModel.navigateToStudentDetail.observe(viewLifecycleOwner, Observer {student -> + student?.let { + this.findNavController().navigate(StudentListFragmentDirections + .actionStudentListFragmentToStudentDetailFragment(student)) + studentListViewModel.onStudentDetailNavigated() + } + }) + + // Initialize adapter and add to studentList + val adapter = StudentAdapter(StudentListener { + studentId -> studentListViewModel.onNavToStudentDetail(studentId) + }) + binding.studentList.adapter = adapter + + // If we have a list of students, send it to the adapter + studentListViewModel.students.observe(viewLifecycleOwner, Observer { + it?.let { + adapter.submitList(it) + } + }) + + return binding.root + } +} diff --git a/app/src/main/java/com/taymath/tutortoolkit/studentlist/StudentListViewModel.kt b/app/src/main/java/com/taymath/tutortoolkit/studentlist/StudentListViewModel.kt new file mode 100644 index 0000000..5259802 --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/studentlist/StudentListViewModel.kt @@ -0,0 +1,57 @@ +package com.taymath.tutortoolkit.studentlist + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.taymath.tutortoolkit.studentdatabase.Student +import com.taymath.tutortoolkit.studentdatabase.StudentDatabaseDao +import kotlinx.coroutines.* + +class StudentListViewModel( + val database: StudentDatabaseDao, + application: Application +) : AndroidViewModel(application) { + + /** Coroutine setup variables */ + + /** + * viewModelJob allows us to cancel all coroutines started by this ViewModel. + */ + private val viewModelJob = Job() + + // Add variable to navigation signaller + private val _navigateToAddStudent = MutableLiveData() + val navigateToAddStudent: LiveData + get() = _navigateToAddStudent + + private val _navigateToStudentDetail = MutableLiveData() + val navigateToStudentDetail: LiveData + get() = _navigateToStudentDetail + + val students = database.getAllStudents() + + override fun onCleared() { + super.onCleared() + viewModelJob.cancel() + } + + // set navigate to student to be true when clicked + fun onNavToAddStudent() { + _navigateToAddStudent.value = true + } + + // Reset nav value after navigation + fun onAddStudentNavigated() { + _navigateToAddStudent.value = null + } + + fun onNavToStudentDetail(studentId: Long) { + _navigateToStudentDetail.value = studentId + } + + fun onStudentDetailNavigated() { + _navigateToStudentDetail.value = null + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/taymath/tutortoolkit/studentlist/StudentListViewModelFactory.kt b/app/src/main/java/com/taymath/tutortoolkit/studentlist/StudentListViewModelFactory.kt new file mode 100644 index 0000000..50b84e8 --- /dev/null +++ b/app/src/main/java/com/taymath/tutortoolkit/studentlist/StudentListViewModelFactory.kt @@ -0,0 +1,18 @@ +package com.taymath.tutortoolkit.studentlist + +import android.app.Application +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.taymath.tutortoolkit.studentdatabase.StudentDatabaseDao + +class StudentListViewModelFactory( + private val dataSource: StudentDatabaseDao, + private val application: Application) : ViewModelProvider.Factory { + @Suppress("unchecked_cast") + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(StudentListViewModel::class.java)) { + return StudentListViewModel(dataSource, application) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_item_test.xml b/app/src/main/res/drawable-v24/ic_item_test.xml new file mode 100644 index 0000000..da1dcd9 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_item_test.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..ca3826a --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/icon_1.png b/app/src/main/res/drawable/icon_1.png new file mode 100644 index 0000000..39ca33a Binary files /dev/null and b/app/src/main/res/drawable/icon_1.png differ diff --git a/app/src/main/res/drawable/icon_2.png b/app/src/main/res/drawable/icon_2.png new file mode 100644 index 0000000..ec9bddb Binary files /dev/null and b/app/src/main/res/drawable/icon_2.png differ diff --git a/app/src/main/res/drawable/icon_3.png b/app/src/main/res/drawable/icon_3.png new file mode 100644 index 0000000..f1d8ed1 Binary files /dev/null and b/app/src/main/res/drawable/icon_3.png differ diff --git a/app/src/main/res/drawable/icon_4.png b/app/src/main/res/drawable/icon_4.png new file mode 100644 index 0000000..730d8e0 Binary files /dev/null and b/app/src/main/res/drawable/icon_4.png differ diff --git a/app/src/main/res/drawable/icon_5.png b/app/src/main/res/drawable/icon_5.png new file mode 100644 index 0000000..a5f34fe Binary files /dev/null and b/app/src/main/res/drawable/icon_5.png differ diff --git a/app/src/main/res/drawable/icon_6.png b/app/src/main/res/drawable/icon_6.png new file mode 100644 index 0000000..612dab1 Binary files /dev/null and b/app/src/main/res/drawable/icon_6.png differ diff --git a/app/src/main/res/drawable/icon_7.png b/app/src/main/res/drawable/icon_7.png new file mode 100644 index 0000000..747b831 Binary files /dev/null and b/app/src/main/res/drawable/icon_7.png differ diff --git a/app/src/main/res/drawable/icon_8.png b/app/src/main/res/drawable/icon_8.png new file mode 100644 index 0000000..76fea58 Binary files /dev/null and b/app/src/main/res/drawable/icon_8.png differ diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..73c4f2d --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,16 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_add_grade.xml b/app/src/main/res/layout/fragment_add_grade.xml new file mode 100644 index 0000000..0bd2f28 --- /dev/null +++ b/app/src/main/res/layout/fragment_add_grade.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + +