diff --git a/app/src/main/java/be/ugent/sel/studeez/StudeezApp.kt b/app/src/main/java/be/ugent/sel/studeez/StudeezApp.kt index 3f1e371..ff46729 100644 --- a/app/src/main/java/be/ugent/sel/studeez/StudeezApp.kt +++ b/app/src/main/java/be/ugent/sel/studeez/StudeezApp.kt @@ -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 diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalCustomTimer.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalCustomTimer.kt new file mode 100644 index 0000000..7cae544 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalCustomTimer.kt @@ -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 + } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalEndlessTimer.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalEndlessTimer.kt new file mode 100644 index 0000000..45eecda --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalEndlessTimer.kt @@ -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() + } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalPomodoroTimer.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalPomodoroTimer.kt new file mode 100644 index 0000000..ef0bf03 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalPomodoroTimer.kt @@ -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 + } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalTimer.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalTimer.kt new file mode 100644 index 0000000..e6965ab --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/FunctionalTimer.kt @@ -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 + +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/HoursMinutesSeconds.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/HoursMinutesSeconds.kt new file mode 100644 index 0000000..856aa26 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/HoursMinutesSeconds.kt @@ -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 +) \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/Time.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/Time.kt new file mode 100644 index 0000000..ff89516 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_functional/Time.kt @@ -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') + ) + } + +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_info/BreakTimerInfo.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_info/BreakTimerInfo.kt new file mode 100644 index 0000000..735f917 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_info/BreakTimerInfo.kt @@ -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 { + return mapOf( + "type" to "break", + "name" to name, + "description" to description, + "studyTime" to studyTime, + "breakTime" to breakTime, + "repeats" to repeats, + ) + } + +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_info/CustomTimerInfo.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_info/CustomTimerInfo.kt new file mode 100644 index 0000000..5e06536 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_info/CustomTimerInfo.kt @@ -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 { + return mapOf( + "type" to "custom", + "name" to name, + "description" to description, + "studyTime" to studyTime, + ) + } + +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_info/EndlessTimerInfo.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_info/EndlessTimerInfo.kt new file mode 100644 index 0000000..d459a4e --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_info/EndlessTimerInfo.kt @@ -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 { + return mapOf( + "type" to "endless", + "name" to name, + "description" to description + ) + } + +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_info/TimerInfo.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_info/TimerInfo.kt new file mode 100644 index 0000000..343e7e3 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_info/TimerInfo.kt @@ -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 + +} + diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_info/TimerJson.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_info/TimerJson.kt new file mode 100644 index 0000000..37a7b9f --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_info/TimerJson.kt @@ -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 = "" +) diff --git a/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_info/TimerType.kt b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_info/TimerType.kt new file mode 100644 index 0000000..20fb36d --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/data/local/models/timer_info/TimerType.kt @@ -0,0 +1,7 @@ +package be.ugent.sel.studeez.data.local.models.timer_info + +enum class TimerType { + BREAK, + ENDLESS, + CUSTOM +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt b/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt index c82696e..e3c466c 100644 --- a/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt +++ b/app/src/main/java/be/ugent/sel/studeez/di/DatabaseModule.kt @@ -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 } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/ConfigurationService.kt b/app/src/main/java/be/ugent/sel/studeez/domain/ConfigurationService.kt new file mode 100644 index 0000000..26aeba5 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/domain/ConfigurationService.kt @@ -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 + +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/Performance.kt b/app/src/main/java/be/ugent/sel/studeez/domain/Performance.kt deleted file mode 100644 index e987b37..0000000 --- a/app/src/main/java/be/ugent/sel/studeez/domain/Performance.kt +++ /dev/null @@ -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 trace(name: String, block: Trace.() -> T): T = Trace.create(name).trace(block) diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/TimerDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/TimerDAO.kt new file mode 100644 index 0000000..ab3edcb --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/domain/TimerDAO.kt @@ -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> + + fun getAllTimers(): Flow> + + fun saveTimer(newTimer: TimerInfo) + + fun updateTimer(timerInfo: TimerInfo) + + fun deleteTimer(timerInfo: TimerInfo) + +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseConfigurationService.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseConfigurationService.kt new file mode 100644 index 0000000..e3db024 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseConfigurationService.kt @@ -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 { + val jsonString: String = remoteConfig[DEFAULT_TIMERS].asString() + // Json is een lijst van timers + val timerJsonList: List = ToTimerConverter().jsonToTimerJsonList(jsonString) + return ToTimerConverter().convertToTimerInfoList(timerJsonList) + } + + companion object { + private const val DEFAULT_TIMERS = "default_timers" + } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTimerDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTimerDAO.kt new file mode 100644 index 0000000..a25c37c --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseTimerDAO.kt @@ -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> { + return currentUserTimersCollection() + .snapshots() + .map { it.toObjects(TimerJson::class.java) } + .map { ToTimerConverter().convertToTimerInfoList(it) } + } + + override fun getAllTimers(): Flow> { + // Wrap default timers in een flow en combineer met de userTimer flow. + val defaultTimers: List = configurationService.getDefaultTimers() + val defaultTimersFlow: Flow> = flowOf(defaultTimers) + val userTimersFlow: Flow> = 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" + } + +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/ToTimerConverter.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/ToTimerConverter.kt new file mode 100644 index 0000000..021f9a0 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/ToTimerConverter.kt @@ -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 = 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): List { + return timerJsonList.map(this::getTimer) + } + + fun jsonToTimerJsonList(json: String): List { + val type = object : TypeToken>() {}.type + return Gson().fromJson(json, type) + } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt index 7b606ca..a6c8290 100644 --- a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt +++ b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt @@ -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" diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/drawer/DrawerViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/drawer/DrawerViewModel.kt index 9cf7fcc..d3460cf 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/drawer/DrawerViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/drawer/DrawerViewModel.kt @@ -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 } diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeViewModel.kt index 8830dc3..b035223 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeViewModel.kt @@ -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) + } } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionScreen.kt new file mode 100644 index 0000000..e87bac5 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionScreen.kt @@ -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 + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionViewModel.kt new file mode 100644 index 0000000..7326212 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionViewModel.kt @@ -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 + } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/splash/SplashViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/splash/SplashViewModel.kt index 6f0f18c..4e1dfbd 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/splash/SplashViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/splash/SplashViewModel.kt @@ -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 diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_overview/TimerOverviewScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_overview/TimerOverviewScreen.kt new file mode 100644 index 0000000..dcf7516 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_overview/TimerOverviewScreen.kt @@ -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) { } +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_overview/TimerOverviewViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_overview/TimerOverviewViewModel.kt new file mode 100644 index 0000000..c2be1e9 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_overview/TimerOverviewViewModel.kt @@ -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> { + return timerDAO.getUserTimers() + } + + fun getDefaultTimers(): List { + return configurationService.getDefaultTimers() + } + + fun update(timerInfo: TimerInfo) = timerDAO.updateTimer(timerInfo) + + fun delete(timerInfo: TimerInfo) =timerDAO.deleteTimer(timerInfo) + + fun save(timerInfo: TimerInfo) = timerDAO.saveTimer(timerInfo) + + +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0f3f21b..b48d7f8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -61,6 +61,8 @@ Timers + Edit + Add timer Settings