commit
f963256ec2
29 changed files with 699 additions and 38 deletions
|
@ -20,10 +20,12 @@ import be.ugent.sel.studeez.common.snackbar.SnackbarManager
|
|||
import be.ugent.sel.studeez.navigation.StudeezDestinations
|
||||
import be.ugent.sel.studeez.screens.home.HomeScreen
|
||||
import be.ugent.sel.studeez.screens.log_in.LoginScreen
|
||||
import be.ugent.sel.studeez.screens.session.SessionScreen
|
||||
import be.ugent.sel.studeez.screens.profile.EditProfileScreen
|
||||
import be.ugent.sel.studeez.screens.profile.ProfileScreen
|
||||
import be.ugent.sel.studeez.screens.sign_up.SignUpScreen
|
||||
import be.ugent.sel.studeez.screens.splash.SplashScreen
|
||||
import be.ugent.sel.studeez.screens.timer_overview.TimerOverviewScreen
|
||||
import be.ugent.sel.studeez.ui.theme.StudeezTheme
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
|
@ -113,6 +115,14 @@ fun NavGraphBuilder.studeezGraph(appState: StudeezAppstate) {
|
|||
ProfileScreen(open, openAndPopUp)
|
||||
}
|
||||
|
||||
composable(StudeezDestinations.TIMER_OVERVIEW_SCREEN) {
|
||||
TimerOverviewScreen(openAndPopUp)
|
||||
}
|
||||
|
||||
composable(StudeezDestinations.SESSION_SCREEN) {
|
||||
SessionScreen(openAndPopUp)
|
||||
}
|
||||
|
||||
// TODO Timers screen
|
||||
// TODO Settings screen
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package be.ugent.sel.studeez.data.local.models.timer_functional
|
||||
|
||||
class FunctionalCustomTimer(studyTime: Int): FunctionalTimer(studyTime) {
|
||||
|
||||
override fun tick() {
|
||||
if (time.getTime() == 0) {
|
||||
view = "Done!"
|
||||
} else {
|
||||
time.minOne()
|
||||
}
|
||||
}
|
||||
|
||||
override fun hasEnded(): Boolean {
|
||||
return time.getTime() == 0
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package be.ugent.sel.studeez.data.local.models.timer_functional
|
||||
|
||||
class FunctionalEndlessTimer() : FunctionalTimer(0){
|
||||
|
||||
override fun hasEnded(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
time.plusOne()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package be.ugent.sel.studeez.data.local.models.timer_functional
|
||||
|
||||
class FunctionalPomodoroTimer(
|
||||
private var studyTime: Int,
|
||||
private var breakTime: Int, repeats: Int
|
||||
): FunctionalTimer(studyTime) {
|
||||
|
||||
private var breaksRemaining = repeats
|
||||
private var isInBreak = false
|
||||
|
||||
override fun tick() {
|
||||
if (time.getTime() == 0 && breaksRemaining == 0){
|
||||
view = "Done!"
|
||||
return
|
||||
}
|
||||
|
||||
if (time.getTime() == 0) {
|
||||
if (isInBreak) {
|
||||
breaksRemaining--
|
||||
view = "Focus! ($breaksRemaining breaks remaining)"
|
||||
time.setTime(studyTime)
|
||||
} else {
|
||||
view = "Take a break!"
|
||||
time.setTime(breakTime)
|
||||
}
|
||||
isInBreak = !isInBreak
|
||||
}
|
||||
time.minOne()
|
||||
}
|
||||
|
||||
override fun hasEnded(): Boolean {
|
||||
return breaksRemaining == 0 && time.getTime() == 0
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package be.ugent.sel.studeez.data.local.models.timer_functional
|
||||
|
||||
abstract class FunctionalTimer(initialValue: Int) {
|
||||
protected val time: Time = Time(initialValue)
|
||||
protected var view: String = "Focus"
|
||||
|
||||
fun getHoursMinutesSeconds(): HoursMinutesSeconds {
|
||||
return time.getAsHMS()
|
||||
}
|
||||
|
||||
fun getViewString(): String {
|
||||
return view
|
||||
}
|
||||
|
||||
abstract fun tick()
|
||||
|
||||
abstract fun hasEnded(): Boolean
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package be.ugent.sel.studeez.data.local.models.timer_functional
|
||||
|
||||
data class HoursMinutesSeconds(val hours: String, val minutes: String, val seconds: String
|
||||
)
|
|
@ -0,0 +1,35 @@
|
|||
package be.ugent.sel.studeez.data.local.models.timer_functional
|
||||
|
||||
class Time(initialTime: Int) {
|
||||
|
||||
private var time = initialTime
|
||||
|
||||
fun minOne() {
|
||||
time--
|
||||
}
|
||||
|
||||
fun plusOne() {
|
||||
time++
|
||||
}
|
||||
|
||||
fun setTime(newTime: Int) {
|
||||
time = newTime
|
||||
}
|
||||
|
||||
fun getTime(): Int {
|
||||
return time
|
||||
}
|
||||
|
||||
fun getAsHMS(): HoursMinutesSeconds {
|
||||
val hours: Int = time / (60 * 60)
|
||||
val minutes: Int = (time / (60)) % 60
|
||||
val seconds: Int = time % 60
|
||||
|
||||
return HoursMinutesSeconds(
|
||||
hours.toString().padStart(2, '0'),
|
||||
minutes.toString().padStart(2, '0'),
|
||||
seconds.toString().padStart(2, '0')
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package be.ugent.sel.studeez.data.local.models.timer_info
|
||||
|
||||
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer
|
||||
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
|
||||
|
||||
class BreakTimerInfo(
|
||||
name: String,
|
||||
description: String,
|
||||
private val studyTime: Int,
|
||||
private val breakTime: Int,
|
||||
private val repeats: Int,
|
||||
id: String = ""
|
||||
): TimerInfo(id, name, description) {
|
||||
|
||||
|
||||
override fun getFunctionalTimer(): FunctionalTimer {
|
||||
return FunctionalPomodoroTimer(studyTime, breakTime, repeats)
|
||||
}
|
||||
|
||||
override fun asJson() : Map<String, Any> {
|
||||
return mapOf(
|
||||
"type" to "break",
|
||||
"name" to name,
|
||||
"description" to description,
|
||||
"studyTime" to studyTime,
|
||||
"breakTime" to breakTime,
|
||||
"repeats" to repeats,
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package be.ugent.sel.studeez.data.local.models.timer_info
|
||||
|
||||
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalCustomTimer
|
||||
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
|
||||
|
||||
class CustomTimerInfo(
|
||||
name: String,
|
||||
description: String,
|
||||
private val studyTime: Int,
|
||||
id: String = ""
|
||||
): TimerInfo(id, name, description) {
|
||||
|
||||
|
||||
override fun getFunctionalTimer(): FunctionalTimer {
|
||||
return FunctionalCustomTimer(studyTime)
|
||||
}
|
||||
|
||||
override fun asJson() : Map<String, Any> {
|
||||
return mapOf(
|
||||
"type" to "custom",
|
||||
"name" to name,
|
||||
"description" to description,
|
||||
"studyTime" to studyTime,
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package be.ugent.sel.studeez.data.local.models.timer_info
|
||||
|
||||
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalEndlessTimer
|
||||
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
|
||||
|
||||
class EndlessTimerInfo(
|
||||
name: String,
|
||||
description: String,
|
||||
id: String = ""
|
||||
): TimerInfo(id, name, description) {
|
||||
|
||||
|
||||
override fun getFunctionalTimer(): FunctionalTimer {
|
||||
return FunctionalEndlessTimer()
|
||||
}
|
||||
|
||||
override fun asJson() : Map<String, Any> {
|
||||
return mapOf(
|
||||
"type" to "endless",
|
||||
"name" to name,
|
||||
"description" to description
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package be.ugent.sel.studeez.data.local.models.timer_info
|
||||
|
||||
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
|
||||
|
||||
/**
|
||||
* Deze klasse stelt de de info van een timer weer. Elke timer heeft een id, naam en descriptie
|
||||
*/
|
||||
abstract class TimerInfo(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val description: String
|
||||
) {
|
||||
|
||||
/**
|
||||
* Geef de functionele timer terug die kan gebruikt worden tijden een sessie.
|
||||
*/
|
||||
abstract fun getFunctionalTimer(): FunctionalTimer
|
||||
|
||||
/**
|
||||
* Geef deze timer weer als json. Wordt gebruikt om terug op te slaan in de databank.
|
||||
* TODO implementaties hebben nog hardgecodeerde strings.
|
||||
*/
|
||||
abstract fun asJson(): Map<String, Any>
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package be.ugent.sel.studeez.data.local.models.timer_info
|
||||
|
||||
import com.google.firebase.firestore.DocumentId
|
||||
|
||||
/**
|
||||
* Timers uit de databank (remote config en firestore) worden als eerste stap omgezet naar dit type.
|
||||
*/
|
||||
data class TimerJson(
|
||||
val type: String = "",
|
||||
val name: String = "",
|
||||
val description: String = "",
|
||||
val studyTime: Int = 0,
|
||||
val breakTime: Int = 0,
|
||||
val repeats: Int = 0,
|
||||
@DocumentId val id: String = ""
|
||||
)
|
|
@ -0,0 +1,7 @@
|
|||
package be.ugent.sel.studeez.data.local.models.timer_info
|
||||
|
||||
enum class TimerType {
|
||||
BREAK,
|
||||
ENDLESS,
|
||||
CUSTOM
|
||||
}
|
|
@ -1,11 +1,7 @@
|
|||
package be.ugent.sel.studeez.di
|
||||
|
||||
import be.ugent.sel.studeez.domain.AccountDAO
|
||||
import be.ugent.sel.studeez.domain.LogService
|
||||
import be.ugent.sel.studeez.domain.UserDAO
|
||||
import be.ugent.sel.studeez.domain.implementation.FirebaseAccountDAO
|
||||
import be.ugent.sel.studeez.domain.implementation.FirebaseUserDAO
|
||||
import be.ugent.sel.studeez.domain.implementation.LogServiceImpl
|
||||
import be.ugent.sel.studeez.domain.*
|
||||
import be.ugent.sel.studeez.domain.implementation.*
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
|
@ -18,6 +14,9 @@ abstract class DatabaseModule {
|
|||
|
||||
@Binds abstract fun provideUserDAO(impl: FirebaseUserDAO): UserDAO
|
||||
|
||||
@Binds abstract fun provideTimerDAO(impl: FirebaseTimerDAO): TimerDAO
|
||||
|
||||
@Binds abstract fun provideLogService(impl: LogServiceImpl): LogService
|
||||
|
||||
@Binds abstract fun provideConfigurationService(impl: FirebaseConfigurationService): ConfigurationService
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package be.ugent.sel.studeez.domain
|
||||
|
||||
import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo
|
||||
|
||||
interface ConfigurationService {
|
||||
|
||||
suspend fun fetchConfiguration(): Boolean
|
||||
|
||||
fun getDefaultTimers(): List<TimerInfo>
|
||||
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
Copyright 2022 Google LLC
|
||||
|
||||
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
|
||||
|
||||
https://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 be.ugent.sel.studeez.domain
|
||||
|
||||
import com.google.firebase.perf.ktx.trace
|
||||
import com.google.firebase.perf.metrics.Trace
|
||||
|
||||
/**
|
||||
* Trace a block with Firebase performance.
|
||||
*
|
||||
* Supports both suspend and regular methods.
|
||||
*/
|
||||
inline fun <T> trace(name: String, block: Trace.() -> T): T = Trace.create(name).trace(block)
|
19
app/src/main/java/be/ugent/sel/studeez/domain/TimerDAO.kt
Normal file
19
app/src/main/java/be/ugent/sel/studeez/domain/TimerDAO.kt
Normal file
|
@ -0,0 +1,19 @@
|
|||
package be.ugent.sel.studeez.domain
|
||||
|
||||
import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo
|
||||
import be.ugent.sel.studeez.data.local.models.timer_info.TimerJson
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface TimerDAO {
|
||||
|
||||
fun getUserTimers(): Flow<List<TimerInfo>>
|
||||
|
||||
fun getAllTimers(): Flow<List<TimerInfo>>
|
||||
|
||||
fun saveTimer(newTimer: TimerInfo)
|
||||
|
||||
fun updateTimer(timerInfo: TimerInfo)
|
||||
|
||||
fun deleteTimer(timerInfo: TimerInfo)
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package be.ugent.sel.studeez.domain.implementation
|
||||
|
||||
import be.ugent.sel.studeez.data.local.models.timer_info.*
|
||||
import be.ugent.sel.studeez.domain.ConfigurationService
|
||||
import com.google.firebase.ktx.Firebase
|
||||
import com.google.firebase.remoteconfig.ktx.get
|
||||
import com.google.firebase.remoteconfig.ktx.remoteConfig
|
||||
import com.google.firebase.remoteconfig.ktx.remoteConfigSettings
|
||||
import com.google.gson.Gson
|
||||
import kotlinx.coroutines.tasks.await
|
||||
import javax.inject.Inject
|
||||
|
||||
class FirebaseConfigurationService @Inject constructor() : ConfigurationService {
|
||||
|
||||
init {
|
||||
// fetch configs elke keer als app wordt opgestart
|
||||
val configSettings = remoteConfigSettings { minimumFetchIntervalInSeconds = 0 }
|
||||
remoteConfig.setConfigSettingsAsync(configSettings)
|
||||
}
|
||||
|
||||
private val remoteConfig
|
||||
get() = Firebase.remoteConfig
|
||||
|
||||
override suspend fun fetchConfiguration(): Boolean {
|
||||
return remoteConfig.fetchAndActivate().await()
|
||||
}
|
||||
|
||||
override fun getDefaultTimers(): List<TimerInfo> {
|
||||
val jsonString: String = remoteConfig[DEFAULT_TIMERS].asString()
|
||||
// Json is een lijst van timers
|
||||
val timerJsonList: List<TimerJson> = ToTimerConverter().jsonToTimerJsonList(jsonString)
|
||||
return ToTimerConverter().convertToTimerInfoList(timerJsonList)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val DEFAULT_TIMERS = "default_timers"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package be.ugent.sel.studeez.domain.implementation
|
||||
|
||||
import be.ugent.sel.studeez.data.local.models.timer_info.*
|
||||
import be.ugent.sel.studeez.data.local.models.timer_info.TimerType.*
|
||||
import be.ugent.sel.studeez.domain.AccountDAO
|
||||
import be.ugent.sel.studeez.domain.TimerDAO
|
||||
import com.google.firebase.firestore.CollectionReference
|
||||
import com.google.firebase.firestore.DocumentSnapshot
|
||||
import com.google.firebase.firestore.FirebaseFirestore
|
||||
import com.google.firebase.firestore.ktx.snapshots
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import javax.inject.Inject
|
||||
|
||||
class FirebaseTimerDAO @Inject constructor(
|
||||
private val firestore: FirebaseFirestore,
|
||||
private val configurationService: FirebaseConfigurationService,
|
||||
private val auth: AccountDAO
|
||||
) : TimerDAO {
|
||||
|
||||
override fun getUserTimers(): Flow<List<TimerInfo>> {
|
||||
return currentUserTimersCollection()
|
||||
.snapshots()
|
||||
.map { it.toObjects(TimerJson::class.java) }
|
||||
.map { ToTimerConverter().convertToTimerInfoList(it) }
|
||||
}
|
||||
|
||||
override fun getAllTimers(): Flow<List<TimerInfo>> {
|
||||
// Wrap default timers in een flow en combineer met de userTimer flow.
|
||||
val defaultTimers: List<TimerInfo> = configurationService.getDefaultTimers()
|
||||
val defaultTimersFlow: Flow<List<TimerInfo>> = flowOf(defaultTimers)
|
||||
val userTimersFlow: Flow<List<TimerInfo>> = getUserTimers()
|
||||
return defaultTimersFlow.combine(userTimersFlow) { defaultTimersList, userTimersList ->
|
||||
defaultTimersList + userTimersList
|
||||
}
|
||||
}
|
||||
|
||||
override fun saveTimer(newTimer: TimerInfo) {
|
||||
currentUserTimersCollection().add(newTimer.asJson())
|
||||
}
|
||||
|
||||
override fun updateTimer(timerInfo: TimerInfo) {
|
||||
currentUserTimersCollection().document(timerInfo.id).set(timerInfo.asJson())
|
||||
}
|
||||
|
||||
override fun deleteTimer(timerInfo: TimerInfo) {
|
||||
currentUserTimersCollection().document(timerInfo.id).delete()
|
||||
}
|
||||
|
||||
private fun currentUserTimersCollection(): CollectionReference =
|
||||
firestore.collection(USER_COLLECTION)
|
||||
.document(auth.currentUserId)
|
||||
.collection(TIMER_COLLECTION)
|
||||
|
||||
|
||||
companion object {
|
||||
private const val TIMER_COLLECTION = "timers"
|
||||
private const val USER_COLLECTION = "users"
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package be.ugent.sel.studeez.domain.implementation
|
||||
|
||||
import be.ugent.sel.studeez.data.local.models.timer_info.*
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
|
||||
/**
|
||||
* Used by ConfigurationService and TimerDAO.
|
||||
*
|
||||
* ConfigurationService: configuration is fetched as a JSON-string,
|
||||
* which is converted into a TimerJson, and converted here into the correct TimerInfo
|
||||
*
|
||||
* timerDAO: Timers are being fetched directly to TinerJson and convertes into the correct timerInfo
|
||||
*/
|
||||
class ToTimerConverter {
|
||||
|
||||
fun interface TimerFactory {
|
||||
fun makeTimer(map: TimerJson) : TimerInfo
|
||||
}
|
||||
|
||||
private val timerInfoMap: Map<TimerType, TimerFactory> = mapOf(
|
||||
TimerType.ENDLESS to TimerFactory { EndlessTimerInfo(
|
||||
it.name,
|
||||
it.description,
|
||||
it.id
|
||||
) },
|
||||
TimerType.CUSTOM to TimerFactory { CustomTimerInfo(
|
||||
it.name,
|
||||
it.description,
|
||||
it.studyTime,
|
||||
it.id
|
||||
) },
|
||||
TimerType.BREAK to TimerFactory { BreakTimerInfo(
|
||||
it.name,
|
||||
it.description,
|
||||
it.studyTime,
|
||||
it.breakTime,
|
||||
it.repeats,
|
||||
it.id
|
||||
) }
|
||||
)
|
||||
|
||||
private fun getTimer(timerJson: TimerJson): TimerInfo{
|
||||
val type: TimerType = TimerType.valueOf(timerJson.type.uppercase())
|
||||
return timerInfoMap.getValue(type).makeTimer(timerJson)
|
||||
}
|
||||
|
||||
fun convertToTimerInfoList(timerJsonList: List<TimerJson>): List<TimerInfo> {
|
||||
return timerJsonList.map(this::getTimer)
|
||||
}
|
||||
|
||||
fun jsonToTimerJsonList(json: String): List<TimerJson> {
|
||||
val type = object : TypeToken<List<TimerJson>>() {}.type
|
||||
return Gson().fromJson(json, type)
|
||||
}
|
||||
}
|
|
@ -5,9 +5,11 @@ object StudeezDestinations {
|
|||
const val SIGN_UP_SCREEN = "signup"
|
||||
const val LOGIN_SCREEN = "login"
|
||||
|
||||
const val HOME_SCREEN = "home"
|
||||
// const val TASKS_SCREEN = "tasks"
|
||||
// const val SESSIONS_SCREEN = "sessions"
|
||||
const val HOME_SCREEN = "home"
|
||||
const val TIMER_OVERVIEW_SCREEN = "timer_overview"
|
||||
const val SESSION_SCREEN = "session"
|
||||
// const val TASKS_SCREEN = "tasks"
|
||||
// const val SESSIONS_SCREEN = "sessions"
|
||||
const val PROFILE_SCREEN = "profile"
|
||||
|
||||
// const val TIMERS_SCREEN = "timers"
|
||||
|
|
|
@ -2,6 +2,7 @@ package be.ugent.sel.studeez.screens.drawer
|
|||
|
||||
import be.ugent.sel.studeez.domain.AccountDAO
|
||||
import be.ugent.sel.studeez.domain.LogService
|
||||
import be.ugent.sel.studeez.navigation.StudeezDestinations
|
||||
import be.ugent.sel.studeez.navigation.StudeezDestinations.HOME_SCREEN
|
||||
import be.ugent.sel.studeez.navigation.StudeezDestinations.LOGIN_SCREEN
|
||||
import be.ugent.sel.studeez.screens.StudeezViewModel
|
||||
|
@ -22,6 +23,11 @@ class DrawerViewModel @Inject constructor(
|
|||
// TODO
|
||||
}
|
||||
|
||||
fun onTimersClick(openAndPopup: (String, String) -> Unit) {
|
||||
// TODO is niet altijd het homescreen
|
||||
openAndPopup(StudeezDestinations.TIMER_OVERVIEW_SCREEN, StudeezDestinations.HOME_SCREEN)
|
||||
}
|
||||
|
||||
fun onSettingsClick(open: (String) -> Unit) {
|
||||
// TODO
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@ package be.ugent.sel.studeez.screens.home
|
|||
|
||||
import be.ugent.sel.studeez.domain.AccountDAO
|
||||
import be.ugent.sel.studeez.domain.LogService
|
||||
import be.ugent.sel.studeez.navigation.StudeezDestinations
|
||||
import be.ugent.sel.studeez.navigation.StudeezDestinations.HOME_SCREEN
|
||||
import be.ugent.sel.studeez.navigation.StudeezDestinations.LOGIN_SCREEN
|
||||
import be.ugent.sel.studeez.screens.StudeezViewModel
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
|
@ -12,7 +15,14 @@ class HomeViewModel @Inject constructor(
|
|||
logService: LogService
|
||||
) : StudeezViewModel(logService) {
|
||||
|
||||
fun onStartSessionClick(open: (String) -> Unit) {
|
||||
// TODO open(StudeezDestinations.xxx, StudeezDestinations.HOME_SCREEN)
|
||||
fun onStartSessionClick(openAndPopUp: (String, String) -> Unit) {
|
||||
openAndPopUp(StudeezDestinations.SESSION_SCREEN, StudeezDestinations.HOME_SCREEN)
|
||||
}
|
||||
|
||||
fun onLogoutClick(openAndPopup: (String, String) -> Unit) {
|
||||
launchCatching {
|
||||
accountDAO.signOut()
|
||||
openAndPopup(LOGIN_SCREEN, HOME_SCREEN)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package be.ugent.sel.studeez.screens.session
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import be.ugent.sel.studeez.R
|
||||
import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate
|
||||
import be.ugent.sel.studeez.resources
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
@Composable
|
||||
fun SessionScreen(
|
||||
openAndPopUp: (String, String) -> Unit,
|
||||
viewModel: SessionViewModel = hiltViewModel()
|
||||
) {
|
||||
PrimaryScreenTemplate(
|
||||
title = resources().getString(R.string.start_session),
|
||||
openAndPopUp = openAndPopUp
|
||||
) {
|
||||
Timer(viewModel)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Timer(viewModel: SessionViewModel = hiltViewModel()) {
|
||||
var tikker by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(tikker) {
|
||||
delay(1000)
|
||||
viewModel.getTimer().tick()
|
||||
tikker = !tikker
|
||||
}
|
||||
|
||||
val hms = viewModel.getTimer().getHoursMinutesSeconds()
|
||||
Column {
|
||||
Text(
|
||||
text = "${hms.hours} : ${hms.minutes} : ${hms.seconds}",
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textAlign = TextAlign.Center,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 80.sp
|
||||
)
|
||||
Text(
|
||||
text = viewModel.getTimer().getViewString(),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textAlign = TextAlign.Center,
|
||||
fontWeight = FontWeight.Light,
|
||||
fontSize = 30.sp
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package be.ugent.sel.studeez.screens.session
|
||||
|
||||
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer
|
||||
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
|
||||
import be.ugent.sel.studeez.domain.LogService
|
||||
import be.ugent.sel.studeez.screens.StudeezViewModel
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class SessionViewModel @Inject constructor(
|
||||
logService: LogService
|
||||
) : StudeezViewModel(logService) {
|
||||
|
||||
private val timer: FunctionalTimer = FunctionalPomodoroTimer(15, 5, 3)
|
||||
|
||||
fun getTimer() : FunctionalTimer {
|
||||
return timer
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package be.ugent.sel.studeez.screens.splash
|
|||
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import be.ugent.sel.studeez.domain.AccountDAO
|
||||
import be.ugent.sel.studeez.domain.ConfigurationService
|
||||
import be.ugent.sel.studeez.domain.LogService
|
||||
import be.ugent.sel.studeez.navigation.StudeezDestinations
|
||||
import be.ugent.sel.studeez.screens.StudeezViewModel
|
||||
|
@ -11,10 +12,15 @@ import javax.inject.Inject
|
|||
@HiltViewModel
|
||||
class SplashViewModel @Inject constructor(
|
||||
private val accountDAO: AccountDAO,
|
||||
private val configurationService: ConfigurationService,
|
||||
logService: LogService
|
||||
) : StudeezViewModel(logService) {
|
||||
val showError = mutableStateOf(false)
|
||||
|
||||
init {
|
||||
launchCatching { configurationService.fetchConfiguration() }
|
||||
}
|
||||
|
||||
fun onAppStart(openAndPopUp: (String, String) -> Unit) {
|
||||
|
||||
showError.value = false
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
package be.ugent.sel.studeez.screens.timer_overview
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import be.ugent.sel.studeez.R
|
||||
import be.ugent.sel.studeez.common.composable.BasicButton
|
||||
import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate
|
||||
import be.ugent.sel.studeez.common.ext.basicButton
|
||||
import be.ugent.sel.studeez.common.ext.card
|
||||
import be.ugent.sel.studeez.data.local.models.timer_info.CustomTimerInfo
|
||||
import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo
|
||||
import be.ugent.sel.studeez.resources
|
||||
|
||||
@Composable
|
||||
fun TimerOverviewScreen(
|
||||
openAndPopUp: (String, String) -> Unit,
|
||||
viewModel: TimerOverviewViewModel = hiltViewModel()
|
||||
) {
|
||||
|
||||
val timers = viewModel.getUserTimers().collectAsState(initial = emptyList())
|
||||
|
||||
PrimaryScreenTemplate(
|
||||
title = resources().getString(R.string.timers),
|
||||
openAndPopUp = openAndPopUp
|
||||
) {
|
||||
|
||||
Column {
|
||||
LazyColumn(
|
||||
verticalArrangement = Arrangement.spacedBy(7.dp)
|
||||
) {
|
||||
// Default Timers, cannot be edited
|
||||
items(viewModel.getDefaultTimers()) {
|
||||
TimerEntry(timerInfo = it, canEdit = false)
|
||||
}
|
||||
|
||||
// User timers, can be edited
|
||||
items(timers.value) {
|
||||
TimerEntry(timerInfo = it, true) { timerInfo ->
|
||||
viewModel.update(timerInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
BasicButton(R.string.add_timer, Modifier.basicButton()) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TimerEntry(timerInfo: TimerInfo, canEdit: Boolean, update: (TimerInfo) -> Unit = {}) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
text = timerInfo.name,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 20.sp
|
||||
)
|
||||
Text(
|
||||
text = timerInfo.description,
|
||||
fontWeight = FontWeight.Light,
|
||||
fontSize = 15.sp
|
||||
)
|
||||
}
|
||||
if (canEdit) {
|
||||
BasicButton(R.string.edit, Modifier.card()) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun TimerEntryPreview() {
|
||||
val timerInfo = CustomTimerInfo(
|
||||
"my preview timer",
|
||||
"This is the description of the timer",
|
||||
60
|
||||
)
|
||||
TimerEntry(timerInfo = timerInfo, true) { }
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package be.ugent.sel.studeez.screens.timer_overview
|
||||
|
||||
import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo
|
||||
import be.ugent.sel.studeez.domain.ConfigurationService
|
||||
import be.ugent.sel.studeez.domain.LogService
|
||||
import be.ugent.sel.studeez.domain.TimerDAO
|
||||
import be.ugent.sel.studeez.screens.StudeezViewModel
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class TimerOverviewViewModel @Inject constructor(
|
||||
private val configurationService: ConfigurationService,
|
||||
private val timerDAO: TimerDAO,
|
||||
logService: LogService
|
||||
) : StudeezViewModel(logService) {
|
||||
|
||||
fun getUserTimers() : Flow<List<TimerInfo>> {
|
||||
return timerDAO.getUserTimers()
|
||||
}
|
||||
|
||||
fun getDefaultTimers(): List<TimerInfo> {
|
||||
return configurationService.getDefaultTimers()
|
||||
}
|
||||
|
||||
fun update(timerInfo: TimerInfo) = timerDAO.updateTimer(timerInfo)
|
||||
|
||||
fun delete(timerInfo: TimerInfo) =timerDAO.deleteTimer(timerInfo)
|
||||
|
||||
fun save(timerInfo: TimerInfo) = timerDAO.saveTimer(timerInfo)
|
||||
|
||||
|
||||
}
|
|
@ -61,6 +61,8 @@
|
|||
|
||||
<!-- Timers -->
|
||||
<string name="timers">Timers</string>
|
||||
<string name="edit">Edit</string>
|
||||
<string name="add_timer">Add timer</string>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="settings">Settings</string>
|
||||
|
|
Reference in a new issue