Commit 86fdf4aa authored by Filip Wiesner's avatar Filip Wiesner

Model deletion, cleanup

parent ad8d76a4
......@@ -36,11 +36,6 @@ kapt {
}
dependencies {
def anko_version = "0.10.5"
def room_version = "2.1.0-alpha04"
def dagger_version = "2.16"
def nav_version = "1.0.0-beta02"
implementation fileTree(include: ["*.jar"], dir: "libs")
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "androidx.appcompat:appcompat:1.0.2"
......@@ -55,22 +50,17 @@ dependencies {
androidTestImplementation "androidx.test.espresso:espresso-core:3.1.1"
//Tools
implementation "org.jetbrains.anko:anko-commons:$anko_version"
implementation "org.jetbrains.anko:anko-commons:0.10.5"
implementation "androidx.core:core-ktx:1.0.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.27.0-eap13"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.github.magneticflux:kotlin-livedata-utils:0.3.3"
//Navigation
implementation "android.arch.navigation:navigation-fragment-ktx:$nav_version"
implementation "android.arch.navigation:navigation-ui-ktx:$nav_version"
implementation "android.arch.navigation:navigation-fragment-ktx:1.0.0-beta02"
implementation "android.arch.navigation:navigation-ui-ktx:1.0.0-beta02"
//Dependency injection
implementation "com.google.dagger:dagger:$dagger_version"
implementation "com.google.dagger:dagger-android:$dagger_version"
kapt "com.google.dagger:dagger-compiler:$dagger_version"
kapt "com.google.dagger:dagger-android-processor:$dagger_version"
implementation "org.kodein.di:kodein-di-generic-jvm:6.1.0"
// Networking
......@@ -95,9 +85,9 @@ dependencies {
implementation "androidx.dynamicanimation:dynamicanimation:1.0.0"
// Database
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-runtime:2.1.0-alpha04"
implementation "androidx.paging:paging-runtime:2.1.0"
kapt "androidx.room:room-compiler:$room_version"
kapt "androidx.room:room-compiler:2.1.0-alpha04"
// Debug
debugImplementation "com.willowtreeapps.hyperion:hyperion-core:0.9.23"
......
......@@ -2,12 +2,7 @@ package com.cvut.blackbird
import android.app.Application
import android.util.Log
import androidx.fragment.app.FragmentManager
import com.chibatching.kotpref.Kotpref
import com.cvut.blackbird.dinjection.components.ApplicationComponent
import com.cvut.blackbird.dinjection.components.DaggerApplicationComponent
import com.cvut.blackbird.dinjection.modules.RoomModule
import com.cvut.blackbird.dinjection.modules.ServicesModule
import com.cvut.blackbird.dinjection.setupDI
import net.danlew.android.joda.JodaTimeAndroid
import org.kodein.di.Kodein
......@@ -16,8 +11,6 @@ import org.kodein.di.Kodein
class BlackBirdAC: Application() {
companion object {
const val LOG_TAG = "BLACK_BIRD"
lateinit var graph: ApplicationComponent
lateinit var kodein: Kodein
fun log(message: String) = Log.i(LOG_TAG, message)
......@@ -25,13 +18,8 @@ class BlackBirdAC: Application() {
override fun onCreate() {
super.onCreate()
// FragmentManager.enableDebugLogging(true)
// FragmentManager.enableDebugLogging(true)
kodein = setupDI()
// graph = DaggerApplicationComponent.builder()
// .roomModule(RoomModule(this))
// .servicesModule(ServicesModule(this))
// .build()
JodaTimeAndroid.init(this)
Kotpref.init(this)
......
package com.cvut.blackbird.dinjection
import javax.inject.Qualifier
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class ForApplication
package com.cvut.blackbird.dinjection.components
import com.cvut.blackbird.dinjection.modules.ContextModule
import com.cvut.blackbird.dinjection.modules.ServicesModule
import com.cvut.blackbird.dinjection.modules.RoomModule
import com.cvut.blackbird.model.BlackBirdModel
import com.cvut.blackbird.model.flows.*
import dagger.Component
import javax.inject.Singleton
@Singleton
@Component(modules = [ContextModule::class, RoomModule::class, ServicesModule::class])
interface ApplicationComponent {
fun inject(model: BlackBirdModel)
fun inject(model: TasksModel)
fun inject(model: TimetableModel)
fun inject(model: SearchModel)
fun inject(model: ProfileModel)
fun inject(model: EventDetailModel)
}
\ No newline at end of file
package com.cvut.blackbird.dinjection.modules
import android.app.Application
import com.cvut.blackbird.dinjection.ForApplication
import dagger.Module
import javax.inject.Singleton
import dagger.Provides
@Module
class ContextModule(private val application: Application) {
@Provides
@Singleton
@ForApplication
internal fun providesApplicationContext() = application
}
\ No newline at end of file
package com.cvut.blackbird.dinjection.modules
import android.content.Context
import dagger.Provides
import javax.inject.Singleton
import androidx.room.Room
import com.cvut.blackbird.model.database.BlackBirdDB
import com.cvut.blackbird.model.database.CourseDao
import com.cvut.blackbird.model.database.EventDao
import dagger.Module
@Module
class RoomModule(context: Context) {
private val db: BlackBirdDB = Room
.databaseBuilder(context, BlackBirdDB::class.java, "blackbird_DB")
.fallbackToDestructiveMigration()
.build()
@Singleton
@Provides
internal fun providesRoomDatabase() = db
@Singleton
@Provides
internal fun providesEventDao(db: BlackBirdDB) = db.eventDao()
@Singleton
@Provides
internal fun providesCourseDao(db: BlackBirdDB) = db.courseDao()
@Singleton
@Provides
internal fun providesTeacherDao(db: BlackBirdDB) = db.teacherDao()
}
\ No newline at end of file
package com.cvut.blackbird.dinjection.modules
import android.content.Context
import com.cvut.blackbird.model.services.AuthService
import com.cvut.blackbird.model.services.KosService
import com.cvut.blackbird.model.services.SiriusService
import com.google.gson.GsonBuilder
import com.google.gson.JsonDeserializer
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializer
import com.tickaroo.tikxml.TikXml
import com.tickaroo.tikxml.retrofit.TikXmlConverterFactory
import dagger.Module
import dagger.Provides
import org.joda.time.DateTime
import org.joda.time.format.ISODateTimeFormat
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import javax.inject.Singleton
@Module
class ServicesModule(val context: Context) {
private val fmt = ISODateTimeFormat.dateTime()
private val dateTimeSer: JsonSerializer<DateTime> = JsonSerializer { src, _, _ ->
if (src == null) null else JsonPrimitive(src.millis)
}
private val dateTimeDeser: JsonDeserializer<DateTime> = JsonDeserializer<DateTime> { json, _, _ ->
if (json == null) null else
try { fmt.parseDateTime(json.asString)
} catch (e: UnsupportedOperationException) { DateTime(json.asLong) }
}
@Provides
@Singleton
fun providesAuthService(): AuthService {
return Retrofit.Builder().apply {
baseUrl(AuthService.url)
addConverterFactory(GsonConverterFactory.create())
}.build().create(AuthService::class.java)
}
@Provides
@Singleton
fun providesKosService(): KosService {
return Retrofit.Builder().apply {
baseUrl(KosService.url)
addConverterFactory(TikXmlConverterFactory.create(TikXml.Builder()
.exceptionOnUnreadXml(false)
.build()))
}.build().create(KosService::class.java)
}
@Provides
@Singleton
fun providesSiriusService(): SiriusService {
return Retrofit.Builder().apply {
baseUrl(SiriusService.url)
addConverterFactory(GsonConverterFactory.create(
GsonBuilder()
.registerTypeAdapter(DateTime::class.java, dateTimeSer)
.registerTypeAdapter(DateTime::class.java, dateTimeDeser)
.create()
))
}.build().create(SiriusService::class.java)
}
}
\ No newline at end of file
package com.cvut.blackbird.flows
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.ViewModelProviders
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.NavController
import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment
import com.cvut.blackbird.R
import com.cvut.blackbird.flows.detail.EventDetailViewModel
import com.cvut.blackbird.flows.profile.ProfileViewModel
import com.cvut.blackbird.flows.search.SearchViewModel
import com.cvut.blackbird.flows.tasks.TasksViewModel
import com.cvut.blackbird.flows.timetable.TimetableViewModel
import com.cvut.blackbird.model.services.UserInfo
import kotlinx.android.synthetic.main.black_bird_ac_activity.*
......
......@@ -9,10 +9,6 @@ import com.cvut.blackbird.model.database.EventDao
import com.cvut.blackbird.model.database.TeacherDao
import com.cvut.blackbird.model.entities.DetailedEvent
import com.cvut.blackbird.model.entities.Event
import com.cvut.blackbird.model.flows.EventDetailModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.android.Main
import kotlinx.coroutines.withContext
import org.kodein.di.generic.instance
class EventDetailViewModel: BlackBirdVM() {
......
......@@ -10,7 +10,6 @@ import com.cvut.blackbird.flows.BlackBirdVM
import com.cvut.blackbird.model.NotYet
import com.cvut.blackbird.model.Result
import com.cvut.blackbird.model.Success
import com.cvut.blackbird.model.flows.ProfileModel
import com.cvut.blackbird.model.services.local.WipeData
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
......
package com.cvut.blackbird.flows.tasks
import androidx.lifecycle.*
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.chibatching.kotpref.livedata.asLiveData
import com.cvut.blackbird.BlackBirdAC
import com.cvut.blackbird.extensions.*
import com.cvut.blackbird.flows.BlackBirdVM
import com.cvut.blackbird.model.*
import com.cvut.blackbird.model.Failure
import com.cvut.blackbird.model.NotYet
import com.cvut.blackbird.model.Result
import com.cvut.blackbird.model.Success
import com.cvut.blackbird.model.database.EventDao
import com.cvut.blackbird.model.entities.DetailedEvent
import com.cvut.blackbird.model.entities.Event
import com.cvut.blackbird.model.entities.News
import com.cvut.blackbird.model.flows.TasksModel
import com.cvut.blackbird.model.services.EventsMeta
import com.cvut.blackbird.model.services.kos.LoadEvents
import com.cvut.blackbird.model.services.other.FetchNews
import com.cvut.blackbird.support.glue.map
import com.cvut.blackbird.support.glue.switchMap
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.android.Main
......@@ -34,8 +35,7 @@ class TasksViewModel : BlackBirdVM() {
private val _refreshResult = MutableLiveData<Result<Unit>>() withDefault NotYet()
private val _tasks = MutableLiveData<List<Task>>() withDefault listOf()
private val _news = MutableLiveData<List<News>>() withDefault listOf()
private var pinned: LiveData<List<DetailedEvent>> = MutableLiveData()
private val pinnedEvents =
EventsMeta.asLiveData(EventsMeta::pinned)
.switchMap { runBlocking(coroutineContext) {
......
package com.cvut.blackbird.model
import com.cvut.blackbird.BlackBirdAC
import com.cvut.blackbird.BlackBirdAC.Companion.log
import com.cvut.blackbird.model.database.EventDao
import com.cvut.blackbird.model.database.insertAll
import com.cvut.blackbird.model.services.AuthInfo
import com.cvut.blackbird.model.services.AuthService
import com.cvut.blackbird.model.services.SiriusService
import com.cvut.blackbird.model.services.UserInfo
import retrofit2.Call
import java.net.HttpURLConnection
import javax.inject.Inject
sealed class Result<T>
class Success<T>(val value: T): Result<T>()
class Failure<T>(val message: String): Result<T>()
class Loading<T>: Result<T>()
class NotYet<T>: Result<T>()
abstract class BlackBirdModel {
@Inject
lateinit var authService: AuthService
init {
BlackBirdAC.graph.inject(this)
}
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
if (events != null)
eventDao.insertAll(events)
return Success(Unit)
} else if (result is Failure) return Failure(result.message)
return Failure("No error message")
}
suspend fun refreshToken(): Result<Unit> {
log("Refreshing token")
return try {
val response = authService.refreshToken().execute()
if (response.isSuccessful && response.body() != null) {
AuthInfo.accessToken = response.body()!!.token
log("Token refreshed")
Success(Unit)
} else {
Failure(response.errorBody()?.string() ?: "No error message")
}
} catch (e: Throwable) {
Failure(e.localizedMessage)
}
}
suspend inline fun <T> fetch(call: () -> Call<T>): Result<T> {
var result: Result<T> = NotYet()
try {
while (result is NotYet) { // repeat until we have result
val response = call().execute()
result = if (response.isSuccessful && response.body() != null)
// if request was successful (code 200 - 299) and body is not empty
Success(response.body()!!)
else {
if (response.code() == HttpURLConnection.HTTP_UNAUTHORIZED
&& refreshToken() is Success)
// if request was rejected because of bad AuthToken but token refresh was successful
// repeat request
NotYet()
else
Failure(response.errorBody()?.string() ?: "No error message")
}
}
} catch (e: Throwable) {
return Failure(e.localizedMessage)
}
return result
}
}
\ No newline at end of file
package com.cvut.blackbird.model
sealed class Result<T>
class Success<T>(val value: T): Result<T>()
class Failure<T>(val message: String): Result<T>()
class Loading<T>: Result<T>()
class NotYet<T>: Result<T>()
\ No newline at end of file
......@@ -42,6 +42,7 @@ interface EventDao {
@Query("DELETE FROM Event")
fun deleteAllEvents()
@Transaction
@Query("SELECT * FROM Event WHERE startsAt > :now ORDER BY startsAt LIMIT 1")
fun nextEvent(now: Long = DateTime.now().millis): DetailedEvent?
......
......@@ -81,7 +81,7 @@ data class Note(
val en: String?
)
enum class EventType(cs: String, en: String) {
enum class EventType(val cs: String, val en: String) {
EXAM("Zkouška","ExamTask"),
LECTURE("Přednáška","Lecture"),
TUTORIAL("Cvičení","Tutorial"),
......
package com.cvut.blackbird.model.flows
import com.cvut.blackbird.BlackBirdAC
import com.cvut.blackbird.model.BlackBirdModel
import com.cvut.blackbird.model.database.EventDao
import com.cvut.blackbird.model.database.TeacherDao
import com.cvut.blackbird.model.entities.EventNote
import javax.inject.Inject
@Deprecated("Use Microservices instead")
class EventDetailModel: BlackBirdModel() {
@Inject lateinit var teacherDao: TeacherDao
@Inject lateinit var eventDao: EventDao
init { BlackBirdAC.graph.inject(this) }
suspend fun getDetailedEvent(eventId: Int) =
eventDao.getDetailedEvent(eventId).apply {
initTeachers(teacherDao)
}
suspend fun updateNote(eventNote: EventNote) {
eventDao.saveEventNote(eventNote)
}
}
\ No newline at end of file
package com.cvut.blackbird.model.flows
import com.cvut.blackbird.BlackBirdAC
import com.cvut.blackbird.model.BlackBirdModel
import com.cvut.blackbird.model.database.CourseDao
import com.cvut.blackbird.model.database.EventDao
import com.cvut.blackbird.model.database.TeacherDao
import com.cvut.blackbird.model.services.AuthInfo
import com.cvut.blackbird.model.services.EventsMeta
import com.cvut.blackbird.model.services.UserInfo
import javax.inject.Inject
@Deprecated("Use Microservices instead")
class ProfileModel: BlackBirdModel() {
@Inject lateinit var eventDao: EventDao
@Inject lateinit var courseDao: CourseDao
@Inject lateinit var teacherDao: TeacherDao
init {
BlackBirdAC.graph.inject(this)
}
suspend fun deleteDB() {
eventDao.deleteAllEvents()
eventDao.deleteAllNotes()
courseDao.deleteAll()
teacherDao.deleteAll()
}
suspend fun deleteSharedPrefs() {
UserInfo.clear()
AuthInfo.clear()
EventsMeta.clear()
}
}
\ No newline at end of file
package com.cvut.blackbird.model.flows
import com.cvut.blackbird.BlackBirdAC
import com.cvut.blackbird.model.BlackBirdModel
import com.cvut.blackbird.model.services.KosService
import javax.inject.Inject
@Deprecated("Use Microservices instead")
class SearchModel: BlackBirdModel() {
@Inject
lateinit var kosService: KosService
init {
BlackBirdAC.graph.inject(this)
}
}
\ No newline at end of file
package com.cvut.blackbird.model.flows
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.chibatching.kotpref.livedata.asLiveData
import com.cvut.blackbird.BlackBirdAC
import com.cvut.blackbird.extensions.passTo
import com.cvut.blackbird.model.BlackBirdModel
import com.cvut.blackbird.model.Failure
import com.cvut.blackbird.model.Result
import com.cvut.blackbird.model.Success
import com.cvut.blackbird.model.database.EventDao
import com.cvut.blackbird.model.entities.DetailedEvent
import com.cvut.blackbird.model.entities.News
import com.cvut.blackbird.model.entities.NewsRoot
import com.cvut.blackbird.model.services.EventsMeta
import com.cvut.blackbird.model.services.SiriusService
import com.tickaroo.tikxml.TikXml
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.android.Main
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import javax.inject.Inject
@Suppress("RedundantSuspendModifier")
@Deprecated("Use Microservices instead")
class TasksModel: BlackBirdModel() {
@Inject lateinit var siriusService: SiriusService
@Inject lateinit var eventDao: EventDao
private val pinnedPref: LiveData<Set<String>>
private val pinnedEvents = MutableLiveData<List<DetailedEvent>>()
private lateinit var detailedEvents: LiveData<List<DetailedEvent>>
init {
BlackBirdAC.graph.inject(this)
pinnedPref = EventsMeta.asLiveData(EventsMeta::pinned)
pinnedPref.observeForever {
GlobalScope.launch { onPinnedChange(it) }
Log.d(BlackBirdAC.LOG_TAG, "PinnedTask changed in size: ${it.size}")}
}
suspend fun getExams() = eventDao.futureExams()
fun getPinnedEvents(): LiveData<List<DetailedEvent>> = pinnedEvents
private suspend fun onPinnedChange(set: Set<String>) {
detailedEvents = eventDao.getDetailedEvents( set.map { it.toInt() })
withContext(Dispatchers.Main) { detailedEvents passTo pinnedEvents }
}
suspend fun refreshEvents() = refreshEvents(siriusService, eventDao)
suspend fun getNews(): Result<List<News>> {
val request = Request.Builder()
.url(NewsRoot.getNewsLink())
.addHeader("charset", "utf-10")
.build()
return try {
val response = OkHttpClient().newCall(request).execute()
if (response.isSuccessful) {
Success(TikXml.Builder()
.exceptionOnUnreadXml(false)