Commit 96101fa5 authored by Filip Wiesner's avatar Filip Wiesner

Minor fixes, prettyfying, BlackBirdVMm, DetEvent

parent 2b0d2c13
......@@ -33,9 +33,10 @@ kapt {
dependencies {
def anko_version = "0.10.5"
def room_version = "2.0.0-rc01"
def room_version = "2.0.0"
def dagger_version = "2.16"
def nav_version = "1.0.0-alpha06"
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.0.0'
......@@ -46,8 +47,8 @@ dependencies {
testImplementation 'junit:junit:4.12'
// Testing
androidTestImplementation 'androidx.test:runner:1.1.0-alpha4'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha4'
androidTestImplementation 'androidx.test:runner:1.1.0-beta01'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-beta01'
//Tools
implementation "org.jetbrains.anko:anko-commons:$anko_version"
......
......@@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 7,
"identityHash": "4accc8019952a9b15793c92e27f562ee",
"identityHash": "daf76b7313a919dccf691f6e1d7adc9f",
"entities": [
{
"tableName": "Event",
......@@ -113,7 +113,7 @@
},
{
"tableName": "EventNote",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`noteId` INTEGER NOT NULL, `eventId` INTEGER NOT NULL, `note` TEXT NOT NULL, PRIMARY KEY(`noteId`))",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`noteId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `eventId` INTEGER NOT NULL, `note` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "noteId",
......@@ -138,7 +138,7 @@
"columnNames": [
"noteId"
],
"autoGenerate": false
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
......@@ -294,7 +294,7 @@
],
"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, \"4accc8019952a9b15793c92e27f562ee\")"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"daf76b7313a919dccf691f6e1d7adc9f\")"
]
}
}
\ No newline at end of file
{
"formatVersion": 1,
"database": {
"version": 8,
"identityHash": "daf76b7313a919dccf691f6e1d7adc9f",
"entities": [
{
"tableName": "Event",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `sequenceNum` INTEGER NOT NULL, `startsAt` INTEGER NOT NULL, `endsAt` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `capacity` INTEGER NOT NULL, `occupied` INTEGER NOT NULL, `eventTypeRaw` TEXT NOT NULL, `parallel` TEXT NOT NULL, `cs` TEXT, `en` TEXT, `course` TEXT, `room` TEXT, `teachers` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "sequenceNum",
"columnName": "sequenceNum",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "startsAt",
"columnName": "startsAt",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "endsAt",
"columnName": "endsAt",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "deleted",
"columnName": "deleted",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "capacity",
"columnName": "capacity",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "occupied",
"columnName": "occupied",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "eventTypeRaw",
"columnName": "eventTypeRaw",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "parallel",
"columnName": "parallel",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "note.cs",
"columnName": "cs",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "note.en",
"columnName": "en",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "linked.course",
"columnName": "course",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "linked.room",
"columnName": "room",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "linked.teachers",
"columnName": "teachers",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_Event_eventTypeRaw",
"unique": false,
"columnNames": [
"eventTypeRaw"
],
"createSql": "CREATE INDEX `index_Event_eventTypeRaw` ON `${TABLE_NAME}` (`eventTypeRaw`)"
}
],
"foreignKeys": []
},
{
"tableName": "EventNote",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`noteId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `eventId` INTEGER NOT NULL, `note` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "noteId",
"columnName": "noteId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "eventId",
"columnName": "eventId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "note",
"columnName": "note",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"noteId"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "Course",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`code` TEXT NOT NULL, `completion` TEXT NOT NULL, `credits` INTEGER NOT NULL, `department` TEXT NOT NULL, `homepage` TEXT, `name` TEXT NOT NULL, `range` TEXT NOT NULL, `season` TEXT NOT NULL, `state` TEXT NOT NULL, `studyForm` TEXT, PRIMARY KEY(`code`))",
"fields": [
{
"fieldPath": "code",
"columnName": "code",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "completion",
"columnName": "completion",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "credits",
"columnName": "credits",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "department",
"columnName": "department",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "homepage",
"columnName": "homepage",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "range",
"columnName": "range",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "season",
"columnName": "season",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "state",
"columnName": "state",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "studyForm",
"columnName": "studyForm",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"code"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "Teacher",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`firstName` TEXT NOT NULL, `lastName` TEXT NOT NULL, `personalNumber` INTEGER NOT NULL, `titlesPost` TEXT, `titlesPre` TEXT, `username` TEXT NOT NULL, `division` TEXT NOT NULL, `email` TEXT NOT NULL, `extern` INTEGER NOT NULL, `phone` TEXT, PRIMARY KEY(`username`))",
"fields": [
{
"fieldPath": "firstName",
"columnName": "firstName",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lastName",
"columnName": "lastName",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "personalNumber",
"columnName": "personalNumber",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "titlesPost",
"columnName": "titlesPost",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "titlesPre",
"columnName": "titlesPre",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "username",
"columnName": "username",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "division",
"columnName": "division",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "email",
"columnName": "email",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "extern",
"columnName": "extern",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "phone",
"columnName": "phone",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"username"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"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, \"daf76b7313a919dccf691f6e1d7adc9f\")"
]
}
}
\ No newline at end of file
......@@ -22,6 +22,8 @@ infix fun<T> SuperGlue<Result<T>>.toNotifyOnSuccess(action: () -> Unit) = this.a
infix fun<T> MutableLiveData<T>.withDefault(init: T) = apply { value = init }
fun <T> LiveData<T>.haveValue() = value != null
fun <T> LiveData<T>.dontHaveValue() = value == null
inline fun MutableLiveData<Boolean>.asBoolProgressStatus(job: () -> Unit) {
......@@ -149,7 +151,7 @@ fun <A, B, C, D, R> combineLiveData(
*/
enum class JobStrategy { CANCEL, EXIT }
inline fun doWith(job: Job?, strategy: JobStrategy = JobStrategy.CANCEL, crossinline todo: () -> Job): Job? {
inline fun doWith(job: Job?, strategy: JobStrategy = JobStrategy.CANCEL, crossinline todo: () -> Job): Job {
if (job?.isActive == true) {
when (strategy) {
JobStrategy.CANCEL -> job.cancel()
......
package com.cvut.blackbird.flows
import androidx.lifecycle.ViewModel
import com.cvut.blackbird.extensions.JobStrategy
import com.cvut.blackbird.extensions.doWith
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.launch
sealed class LateInit<T>
class Initialized<T>(val data: T): LateInit<T>()
open class BlackBirdVM: ViewModel() {
private val jobs = hashMapOf<String, Job>()
fun startJob(
key: String = "undefined",
strategy: JobStrategy = JobStrategy.CANCEL,
work: suspend ()->Unit
) {
if (jobs.containsKey(key))
jobs[key] = doWith(jobs[key], strategy) { launch { work() } }
else
jobs[key] = launch { work() }
}
}
\ No newline at end of file
......@@ -58,7 +58,7 @@ class AuthViewModel : ViewModel() {
} else return@fetchUsing user as Failure
val teachers = if (events is Success) {
_userInitState.postValue("Downloading all your teachersSet")
_userInitState.postValue("Downloading all your teachers")
model.loadTeachers()
} else return@fetchUsing events as Failure
......
......@@ -14,13 +14,13 @@ import com.cvut.blackbird.extensions.courseAbbr
import com.cvut.blackbird.extensions.getColor
import com.cvut.blackbird.model.entities.*
import com.cvut.blackbird.support.glue.*
import kotlinx.android.synthetic.main.fragment_timetable_detail.*
import kotlinx.android.synthetic.main.event_detail_fragment.*
class EventDetailFragment : Fragment() {
lateinit var detailedEvent: DetailedEvent
private lateinit var viewModel: EventDetailViewModel
private lateinit var viewModel: EventDetailVM
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
......@@ -29,29 +29,24 @@ class EventDetailFragment : Fragment() {
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?
= inflater.inflate(R.layout.fragment_timetable_detail, container, false)
= inflater.inflate(R.layout.event_detail_fragment, container, false)
private fun setupBinding() {
bind(viewModel.detailedEvent) toPassValueTo ::setupEvent
// observe(viewModel.detailedEvent) { viewModel
// .requestCourse(detailedEvent.linked?.course ?: "") }
// observe(viewModel.detailedEvent) {if (detailedEvent.linked?.teachersSet != null) viewModel
// .requestTeachers(detailedEvent.linked!!.teachersSet!!) }
// bind(viewModel.course) toPassValueTo ::setupCourse
// bind(viewModel.teacher) toPassValueTo ::setupTeachers
bind(detail_pin) clickToInvoke ::onPinnedStateChange
bindText(detail_notePersonalContent) changeTo viewModel::changeEventNote
// bindText(detail_notePersonalContent) changeTo viewModel::changeEventNote
}
private fun setupEvent(event: DetailedEvent) {
this.detailedEvent = event
detail_abbr.background = when(event.event.eventType) {
EventType.LECTURE -> context?.resources?.getDrawable(R.drawable.ic_timetable_lecture, context?.theme)
EventType.TUTORIAL -> context?.resources?.getDrawable(R.drawable.ic_timetable_tutorial, context?.theme)
else -> context?.resources?.getDrawable(R.drawable.ic_timetable_info, context?.theme)
EventType.LECTURE -> context?.resources?.getDrawable(
R.drawable.ic_timetable_lecture, context?.theme)
EventType.TUTORIAL -> context?.resources?.getDrawable(
R.drawable.ic_timetable_tutorial, context?.theme)
else -> context?.resources?.getDrawable(
R.drawable.ic_timetable_info, context?.theme)
}
detail_abbr.text = event.courseEnt?.code?.courseAbbr ?: "-"
......@@ -64,6 +59,7 @@ class EventDetailFragment : Fragment() {
detail_parallelContent.text = if (event.event.parallel.isNotBlank()) event.event.parallel else "-"
detail_roomContent.text = event.event.linked?.room ?: "None"
detail_noteContent.text = event.event.note?.cs ?: "None"
detail_notePersonalContent.setText(event.userNotes.firstOrNull()?.note ?: "")
setupCourse(event.courseEnt)
setupTeachers(event.teacherList)
......
package com.cvut.blackbird.flows.detail
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.cvut.blackbird.BlackBirdAC
import com.cvut.blackbird.extensions.dontHaveValue
import com.cvut.blackbird.extensions.withDefault
import com.cvut.blackbird.model.entities.Course
import com.cvut.blackbird.flows.BlackBirdVM
import com.cvut.blackbird.model.entities.DetailedEvent
import com.cvut.blackbird.model.entities.Event
import com.cvut.blackbird.model.entities.Teacher
import com.cvut.blackbird.model.flows.EventDetailModel
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.launch
class EventDetailViewModel: ViewModel() {
interface EventDetailVM {
val detailedEvent: LiveData<DetailedEvent>
fun setEvent(event: Event)
fun changeEventNote(text: String)
}
class EventDetailViewModel: BlackBirdVM(), EventDetailVM {
private val model = EventDetailModel()
//TODO: Implement Event init State so you don't have to write "detailedEvent.value!!"
/**
* Observables
*/
private val _detailedEvent = MutableLiveData<DetailedEvent>() withDefault DetailedEvent.empty()
val detailedEvent: LiveData<DetailedEvent> get() = _detailedEvent
override val detailedEvent: LiveData<DetailedEvent> get() = _detailedEvent
/**
* INPUT
*/
var job: Job? = null
fun setEvent(event: Event) {
job?.cancel()
job = launch {
_detailedEvent.postValue( model.getDetailedEvent(event.id) )
}
override fun setEvent(event: Event) = startJob("set") {
_detailedEvent.postValue( model.getDetailedEvent(event.id) )
}
fun changeEventNote(note: String) {
override fun changeEventNote(text: String) = startJob("note") {
if (detailedEvent.dontHaveValue()) return@startJob
val note = if (detailedEvent.value?.userNotes?.firstOrNull() != null)
detailedEvent.value!!.userNotes.first()
else
model.createNote(detailedEvent.value!!.event.id)
// TODO: You will create note every time because the change in DB wont propagate here (event is not LD)
model.updateNote(note.apply { this.note = text })
}
}
\ No newline at end of file
......@@ -25,7 +25,7 @@ import com.cvut.blackbird.support.glue.toPassValueTo
import com.cvut.blackbird.support.glue.withMap
import com.cvut.blackbird.support.wobbly.WobblyAdapter
import com.cvut.blackbird.support.wobbly.WobblyElement
import kotlinx.android.synthetic.main.timetable_dots_fragment.*
import kotlinx.android.synthetic.main.timetable_fragment.*
class TimetableFragment : Fragment() {
......@@ -42,7 +42,7 @@ class TimetableFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.timetable_dots_fragment, container, false)
return inflater.inflate(R.layout.timetable_fragment, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
......
......@@ -24,8 +24,9 @@ abstract class BlackBirdModel {
protected suspend fun refreshEvents(siriusService: SiriusService, eventDao: EventDao): Result<Unit> {
val result = fetch(siriusService.getEvents(UserInfo.username))
if (result is Success) {
val events = result.value.events ?: listOf()
eventDao.insertAll(events)
val events = result.value.events
if (events != null)