Add announcements to compose

This commit is contained in:
Sebastian Seedorf
2021-11-19 18:20:03 +01:00
parent 8302884fdc
commit cb905fc9a6
22 changed files with 511 additions and 57 deletions

View File

@@ -80,14 +80,14 @@ dependencies {
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
// Compose
implementation "androidx.compose.runtime:runtime:$compose_version"
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.ui:ui-tooling:$compose_version"
implementation "androidx.compose.foundation:foundation:$compose_version"
implementation "androidx.compose.foundation:foundation-layout:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.runtime:runtime:1.0.5"
implementation "androidx.compose.runtime:runtime-livedata:1.0.5"
implementation "androidx.compose.ui:ui:1.0.5"
implementation "androidx.compose.ui:ui-tooling:1.0.5"
implementation "androidx.compose.foundation:foundation:1.0.5"
implementation "androidx.compose.foundation:foundation-layout:1.0.5"
implementation "androidx.compose.material:material:1.0.5"
implementation "androidx.navigation:navigation-compose:2.4.0-beta02"
implementation "com.google.android.material:compose-theme-adapter:$compose_version"
implementation "com.google.android.material:compose-theme-adapter:1.1.0"
}

View File

@@ -9,11 +9,11 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.google.android.material.composethemeadapter.MdcTheme
import de.sebse.fuplanner2.auth.AppAccounts
import de.sebse.fuplanner2.database.AppDatabase
import de.sebse.fuplanner2.database.Course
import de.sebse.fuplanner2.database.User
import de.sebse.fuplanner2.ui.theme.AppTheme
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
@@ -25,7 +25,7 @@ class MainActivity() : AppCompatActivity() {
super.onCreate(savedInstanceState)
activityViewModel = ViewModelProvider(this)[MainActivityViewModel::class.java]
setContent {
MdcTheme {
AppTheme {
MainActivityComposable()
}
}

View File

@@ -1,8 +1,8 @@
package de.sebse.fuplanner2.database
import androidx.lifecycle.LiveData
import androidx.paging.DataSource
import androidx.room.*
import de.sebse.fuplanner2.utils.console
@Dao
interface AnnouncementDao {
@@ -12,6 +12,9 @@ interface AnnouncementDao {
@Query("SELECT * FROM announcement WHERE courseId = :courseId ORDER BY createdOn ASC")
fun getAll2(courseId: Long): List<Announcement>
@Query("SELECT * FROM announcement WHERE courseId = :courseId ORDER BY createdOn ASC")
fun getAll3(courseId: Long): LiveData<List<Announcement>>
@Query("SELECT * FROM announcement WHERE uid = :announcementId LIMIT 1")
fun getAnnouncementById(announcementId: Long): Announcement

View File

@@ -21,7 +21,7 @@ import androidx.navigation.NavHostController
import androidx.navigation.NavOptions
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import com.google.android.material.composethemeadapter.MdcTheme
import de.sebse.fuplanner2.ui.theme.AppTheme
import kotlinx.coroutines.launch
@@ -140,7 +140,7 @@ fun DrawerItems(currentRoute: String?, menu: List<NavLinks>, onItemClick: (route
@Preview
@Composable
fun TopBarPreview() {
MdcTheme {
AppTheme {
TopBar(
title = "A title"
) { }
@@ -150,7 +150,7 @@ fun TopBarPreview() {
@Preview
@Composable
fun DrawerPreview() {
MdcTheme {
AppTheme {
Drawer(currentRoute = "courses", menu = screens) { }
}
}

View File

@@ -31,14 +31,14 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
import com.google.android.material.composethemeadapter.MdcTheme
import de.sebse.fuplanner2.MenuItem
import de.sebse.fuplanner2.R
import de.sebse.fuplanner2.Tools
import de.sebse.fuplanner2.database.Course
import de.sebse.fuplanner2.ui.details.CoursePreviewProvider
import de.sebse.fuplanner2.ui.theme.AppTheme
import de.sebse.fuplanner2.ui.tools.previews.CoursePreviewProvider
import de.sebse.fuplanner2.ui.tools.viewmodels.CoursesViewModel
import de.sebse.fuplanner2.utils.color.getColor
import de.sebse.fuplanner2.viewmodels.CoursesViewModel
@Composable
fun CoursesScreen(tools: Tools) {
@@ -54,7 +54,7 @@ fun CoursesScreen(tools: Tools) {
LaunchedEffect(title) {
tools.setTitle(title)
}
MdcTheme {
AppTheme {
GroupedCourseList(groups = groups) { course ->
course.uid?.let {
Log.d("WHERE TO GO", "${MenuItem.Courses.route}/$it")
@@ -163,7 +163,7 @@ fun CourseItemHint(icon: ImageVector, @StringRes imageAltRes: Int, text: String)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun CourseListPreview() {
MdcTheme {
AppTheme {
CourseList(
CoursePreviewProvider().values.take(3).toList()
) { }
@@ -174,7 +174,7 @@ fun CourseListPreview() {
@Preview
@Composable
fun CourseItemPreview(@PreviewParameter(CoursePreviewProvider::class, 1) course: Course) {
MdcTheme {
AppTheme {
CourseItem(course) {}
}
}

View File

@@ -12,27 +12,34 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.android.material.composethemeadapter.MdcTheme
import de.sebse.fuplanner2.R
import de.sebse.fuplanner2.Tools
import de.sebse.fuplanner2.database.Announcement
import de.sebse.fuplanner2.database.AppDatabase
import de.sebse.fuplanner2.database.Course
import de.sebse.fuplanner2.ui.details.components.AnnouncementItem
import de.sebse.fuplanner2.ui.details.components.LecturerItem
import de.sebse.fuplanner2.ui.details.components.QuickLinks
import de.sebse.fuplanner2.viewmodels.DetailsViewModel
import de.sebse.fuplanner2.viewmodels.DetailsViewModelFactory
import de.sebse.fuplanner2.ui.theme.AppTheme
import de.sebse.fuplanner2.ui.tools.previews.AnnouncementPreviewProvider
import de.sebse.fuplanner2.ui.tools.previews.CoursePreviewProvider
import de.sebse.fuplanner2.ui.tools.viewmodels.DetailsViewModel
import de.sebse.fuplanner2.ui.tools.viewmodels.DetailsViewModelFactory
@Composable
fun CourseDetailsScreen(tools: Tools, id: Long) {
val coursesViewModel: DetailsViewModel = viewModel(factory = DetailsViewModelFactory(id))
val state = coursesViewModel.course.observeAsState()
val announce = AppDatabase.getInstance().announcementDao().getAll3(id).observeAsState()
val title = state.value?.title
LaunchedEffect(title) {
title?.let { tools.setTitle(it) }
}
CourseDetailsScreen(state.value, id)
CourseDetailsScreen(state.value, announce.value, id)
}
@Composable
fun CourseDetailsScreen(course: Course?, id: Long) {
fun CourseDetailsScreen(course: Course?, announcement: List<Announcement>?, id: Long) {
Column {
QuickLinks(courseId = id)
Text(
@@ -44,6 +51,15 @@ fun CourseDetailsScreen(course: Course?, id: Long) {
LecturerItem(lecturer = it, courseTitle = course?.title ?: "")
}
}
Text(
text = stringResource(R.string.announcements),
style = MaterialTheme.typography.h5
)
LazyColumn {
items(announcement ?: listOf()) {
AnnouncementItem(it)
}
}
// TODO: Add latest announcements, current assignments, upcoming events
}
}
@@ -51,7 +67,7 @@ fun CourseDetailsScreen(course: Course?, id: Long) {
@Preview
@Composable
fun CourseDetailsScreenPreview(@PreviewParameter(CoursePreviewProvider::class, 1) course: Course) {
MdcTheme {
CourseDetailsScreen(course, course.uid!!)
AppTheme {
CourseDetailsScreen(course, AnnouncementPreviewProvider().values.take(3).toList(), course.uid!!)
}
}

View File

@@ -0,0 +1,58 @@
package de.sebse.fuplanner2.ui.details.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import de.sebse.fuplanner2.R
import de.sebse.fuplanner2.database.Announcement
import de.sebse.fuplanner2.ui.theme.AppTheme
import de.sebse.fuplanner2.ui.tools.previews.AnnouncementPreviewProvider
import de.sebse.fuplanner2.utils.toDateTimeString
@Composable
fun AnnouncementItem(announcement: Announcement) {
AnnouncementItem(announcement) { }
}
@Composable
fun AnnouncementItem(announcement: Announcement, click: () -> Unit) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(dimensionResource(R.dimen.card_view_margin)),
elevation = dimensionResource(R.dimen.card_view_elevation)
) {
Column(
modifier = Modifier
.clickable(true, onClick = click)
.padding(dimensionResource(R.dimen.card_view_padding))
) {
Text(
text = announcement.title ?: "Title",
style = MaterialTheme.typography.h6
)
Text(
text = announcement.createdOn.toDateTimeString(LocalContext.current) ?: "",
style = MaterialTheme.typography.subtitle1
)
}
}
}
@Preview
@Composable
fun AnnouncementItemPreview(@PreviewParameter(AnnouncementPreviewProvider::class, 5) announcement: Announcement) {
AppTheme {
AnnouncementItem(announcement)
}
}

View File

@@ -1,4 +1,4 @@
package de.sebse.fuplanner2.ui.details
package de.sebse.fuplanner2.ui.details.components
import android.content.Context
import android.content.Intent
@@ -21,9 +21,10 @@ import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import com.google.android.material.composethemeadapter.MdcTheme
import de.sebse.fuplanner2.R
import de.sebse.fuplanner2.database.Lecturer
import de.sebse.fuplanner2.ui.theme.AppTheme
import de.sebse.fuplanner2.ui.tools.previews.LecturerPreviewProvider
@Composable
fun LecturerItem(lecturer: Lecturer, courseTitle: String) {
@@ -84,8 +85,8 @@ fun LecturerItem(lecturer: Lecturer, click: () -> Unit) {
@Preview
@Composable
fun LecturerItemPreview(@PreviewParameter(LecturerPreviewProvider::class, 2) lecturer: Lecturer) {
MdcTheme {
fun LecturerItemPreview(@PreviewParameter(LecturerPreviewProvider::class, 3) lecturer: Lecturer) {
AppTheme {
LecturerItem(lecturer, "Course Name")
}
}

View File

@@ -17,8 +17,8 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.google.android.material.composethemeadapter.MdcTheme
import de.sebse.fuplanner2.R
import de.sebse.fuplanner2.ui.theme.AppTheme
data class QuickLinkProps(@StringRes val name: Int, val route: String)
@@ -64,7 +64,7 @@ fun QuickLinks(courseId: Long) {
@Preview
@Composable
fun QuickLinksPreview() {
MdcTheme {
AppTheme {
QuickLinks(3)
}
}

View File

@@ -1,18 +1,34 @@
package de.sebse.fuplanner2.ui.details_announcements
import android.content.Context
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
import androidx.work.workDataOf
import de.sebse.fuplanner2.auth.AppAccounts
import de.sebse.fuplanner2.database.Announcement
import de.sebse.fuplanner2.database.AppDatabase
import de.sebse.fuplanner2.utils.enqueueOneTimeWork
import de.sebse.fuplanner2.worker.AbstractAccountWorker
import de.sebse.fuplanner2.worker.AnnouncementWorker
class AnnouncementsViewModelFactory(private val courseId: Long): ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T = AnnouncementsViewModel(courseId) as T
}
class AnnouncementsViewModel(courseId: Long) : ViewModel() {
class AnnouncementsViewModel(private val courseId: Long) : ViewModel() {
private val factory = AppDatabase.getInstance().announcementDao().getAll1(courseId)
fun refresh(ctx: Context) {
enqueueOneTimeWork<AnnouncementWorker>(ctx) {
it.setInputData(workDataOf(
AbstractAccountWorker.KEY_ACCOUNT_NAME to AppAccounts.getInstance().selectedAccount?.name,
AbstractAccountWorker.KEY_COURSE_ID to courseId
))
}
}
val events: LiveData<PagedList<Announcement>> = LivePagedListBuilder(factory, 50).build()
}

View File

@@ -15,8 +15,8 @@ import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import de.sebse.fuplanner2.R
import de.sebse.fuplanner2.databinding.FragmentDescriptionBinding
import de.sebse.fuplanner2.viewmodels.DetailsViewModel
import de.sebse.fuplanner2.viewmodels.DetailsViewModelFactory
import de.sebse.fuplanner2.ui.tools.viewmodels.DetailsViewModel
import de.sebse.fuplanner2.ui.tools.viewmodels.DetailsViewModelFactory
class DescriptionFragment : Fragment() {

View File

@@ -0,0 +1,58 @@
package de.sebse.fuplanner2.ui.theme
import androidx.compose.ui.graphics.Color
val md_theme_light_primary = Color(0xFF245fa7)
val md_theme_light_onPrimary = Color(0xFFffffff)
val md_theme_light_primaryContainer = Color(0xFFd4e3ff)
val md_theme_light_onPrimaryContainer = Color(0xFF001b3d)
val md_theme_light_secondary = Color(0xFF4a6800)
val md_theme_light_onSecondary = Color(0xFFffffff)
val md_theme_light_secondaryContainer = Color(0xFFbef43c)
val md_theme_light_onSecondaryContainer = Color(0xFF131f00)
val md_theme_light_tertiary = Color(0xFF8b5000)
val md_theme_light_onTertiary = Color(0xFFffffff)
val md_theme_light_tertiaryContainer = Color(0xFFffdcba)
val md_theme_light_onTertiaryContainer = Color(0xFF2d1600)
val md_theme_light_error = Color(0xFFba1b1b)
val md_theme_light_errorContainer = Color(0xFFffdad4)
val md_theme_light_onError = Color(0xFFffffff)
val md_theme_light_onErrorContainer = Color(0xFF410001)
val md_theme_light_background = Color(0xFFfdfbff)
val md_theme_light_onBackground = Color(0xFF1b1b1d)
val md_theme_light_surface = Color(0xFFfdfbff)
val md_theme_light_onSurface = Color(0xFF1b1b1d)
val md_theme_light_surfaceVariant = Color(0xFFe0e2eb)
val md_theme_light_onSurfaceVariant = Color(0xFF44474f)
val md_theme_light_outline = Color(0xFF74777f)
val md_theme_light_inverseOnSurface = Color(0xFFf1f0f4)
val md_theme_light_inverseSurface = Color(0xFF2f3033)
val md_theme_dark_primary = Color(0xFFa6c8ff)
val md_theme_dark_onPrimary = Color(0xFF003063)
val md_theme_dark_primaryContainer = Color(0xFF00468b)
val md_theme_dark_onPrimaryContainer = Color(0xFFd4e3ff)
val md_theme_dark_secondary = Color(0xFFa3d719)
val md_theme_dark_onSecondary = Color(0xFF253600)
val md_theme_dark_secondaryContainer = Color(0xFF374e00)
val md_theme_dark_onSecondaryContainer = Color(0xFFbef43c)
val md_theme_dark_tertiary = Color(0xFFffb86b)
val md_theme_dark_onTertiary = Color(0xFF4a2800)
val md_theme_dark_tertiaryContainer = Color(0xFF6a3c00)
val md_theme_dark_onTertiaryContainer = Color(0xFFffdcba)
val md_theme_dark_error = Color(0xFFffb4a9)
val md_theme_dark_errorContainer = Color(0xFF930006)
val md_theme_dark_onError = Color(0xFF680003)
val md_theme_dark_onErrorContainer = Color(0xFFffdad4)
val md_theme_dark_background = Color(0xFF1b1b1d)
val md_theme_dark_onBackground = Color(0xFFe3e2e6)
val md_theme_dark_surface = Color(0xFF1b1b1d)
val md_theme_dark_onSurface = Color(0xFFe3e2e6)
val md_theme_dark_surfaceVariant = Color(0xFF44474f)
val md_theme_dark_onSurfaceVariant = Color(0xFFc3c6cf)
val md_theme_dark_outline = Color(0xFF8e919a)
val md_theme_dark_inverseOnSurface = Color(0xFF1b1b1d)
val md_theme_dark_inverseSurface = Color(0xFFe3e2e6)
val seed = Color(0xFF003366)
val error = Color(0xFFba1b1b)

View File

@@ -0,0 +1,102 @@
package de.sebse.fuplanner2.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.MaterialTheme
import androidx.compose.material.darkColors
import androidx.compose.material.lightColors
import androidx.compose.runtime.Composable
// Comments are changes in Material 3 (Material You)
//private val LightThemeColors = lightColorScheme(
private val LightThemeColors = lightColors(
primary = md_theme_light_primary,
onPrimary = md_theme_light_onPrimary,
//primaryContainer = md_theme_light_primaryContainer,
//onPrimaryContainer = md_theme_light_onPrimaryContainer,
primaryVariant = md_theme_light_onPrimaryContainer,
secondary = md_theme_light_secondary,
onSecondary = md_theme_light_onSecondary,
//secondaryContainer = md_theme_light_secondaryContainer,
//onSecondaryContainer = md_theme_light_onSecondaryContainer,
secondaryVariant = md_theme_light_onSecondaryContainer,
//tertiary = md_theme_light_tertiary,
//onTertiary = md_theme_light_onTertiary,
//tertiaryContainer = md_theme_light_tertiaryContainer,
//onTertiaryContainer = md_theme_light_onTertiaryContainer,
error = md_theme_light_error,
onError = md_theme_light_onError,
//errorContainer = md_theme_light_errorContainer,
//onErrorContainer = md_theme_light_onErrorContainer,
background = md_theme_light_background,
onBackground = md_theme_light_onBackground,
surface = md_theme_light_surface,
onSurface = md_theme_light_onSurface,
//surfaceVariant = md_theme_light_surfaceVariant,
//onSurfaceVariant = md_theme_light_onSurfaceVariant,
//outline = md_theme_light_outline,
//inverseOnSurface = md_theme_light_inverseOnSurface,
//inverseSurface = md_theme_light_inverseSurface,
)
//private val DarkThemeColors = darkColorScheme(
private val DarkThemeColors = darkColors(
primary = md_theme_dark_primary,
onPrimary = md_theme_dark_onPrimary,
//primaryContainer = md_theme_dark_primaryContainer,
//onPrimaryContainer = md_theme_dark_onPrimaryContainer,
primaryVariant = md_theme_dark_onPrimaryContainer,
secondary = md_theme_dark_secondary,
onSecondary = md_theme_dark_onSecondary,
//secondaryContainer = md_theme_dark_secondaryContainer,
//onSecondaryContainer = md_theme_dark_onSecondaryContainer,
secondaryVariant = md_theme_dark_onSecondaryContainer,
//tertiary = md_theme_dark_tertiary,
//onTertiary = md_theme_dark_onTertiary,
//tertiaryContainer = md_theme_dark_tertiaryContainer,
//onTertiaryContainer = md_theme_dark_onTertiaryContainer,
error = md_theme_dark_error,
onError = md_theme_dark_onError,
//errorContainer = md_theme_dark_errorContainer,
//onErrorContainer = md_theme_dark_onErrorContainer,
background = md_theme_dark_background,
onBackground = md_theme_dark_onBackground,
surface = md_theme_dark_surface,
onSurface = md_theme_dark_onSurface,
//surfaceVariant = md_theme_dark_surfaceVariant,
//onSurfaceVariant = md_theme_dark_onSurfaceVariant,
//outline = md_theme_dark_outline,
//inverseOnSurface = md_theme_dark_inverseOnSurface,
//inverseSurface = md_theme_dark_inverseSurface,
)
@Composable
fun AppTheme(
useDarkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable() () -> Unit
) {
val colors = if (!useDarkTheme) {
LightThemeColors
} else {
DarkThemeColors
}
MaterialTheme(
//colorScheme = colors,
colors = colors,
// typography = AppTypography,
content = content
)
}

View File

@@ -0,0 +1,117 @@
package de.sebse.fuplanner2.ui.theme
// Comments are changes in Material 3 (Material You)
// Use default instead
import androidx.compose.ui.text.font.FontFamily
//Replace with your font locations
val Roboto = FontFamily.Default
/*val AppTypography = Typography(
displayLarge = TextStyle(
fontFamily = Roboto,
fontWeight = FontWeight.W400,
fontSize = 57.sp,
lineHeight = 64.sp,
letterSpacing = -0.25.sp,
),
displayMedium = TextStyle(
fontFamily = Roboto,
fontWeight = FontWeight.W400,
fontSize = 45.sp,
lineHeight = 52.sp,
letterSpacing = 0.sp,
),
displaySmall = TextStyle(
fontFamily = Roboto,
fontWeight = FontWeight.W400,
fontSize = 36.sp,
lineHeight = 44.sp,
letterSpacing = 0.sp,
),
headlineLarge = TextStyle(
fontFamily = Roboto,
fontWeight = FontWeight.W400,
fontSize = 32.sp,
lineHeight = 40.sp,
letterSpacing = 0.sp,
),
headlineMedium = TextStyle(
fontFamily = Roboto,
fontWeight = FontWeight.W400,
fontSize = 28.sp,
lineHeight = 36.sp,
letterSpacing = 0.sp,
),
headlineSmall = TextStyle(
fontFamily = Roboto,
fontWeight = FontWeight.W400,
fontSize = 24.sp,
lineHeight = 32.sp,
letterSpacing = 0.sp,
),
titleLarge = TextStyle(
fontFamily = Roboto,
fontWeight = FontWeight.W400,
fontSize = 22.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp,
),
titleMedium = TextStyle(
fontFamily = Roboto,
fontWeight = FontWeight.Medium,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.1.sp,
),
titleSmall = TextStyle(
fontFamily = Roboto,
fontWeight = FontWeight.Medium,
fontSize = 14.sp,
lineHeight = 20.sp,
letterSpacing = 0.1.sp,
),
labelLarge = TextStyle(
fontFamily = Roboto,
fontWeight = FontWeight.Medium,
fontSize = 14.sp,
lineHeight = 20.sp,
letterSpacing = 0.1.sp,
),
bodyLarge = TextStyle(
fontFamily = Roboto,
fontWeight = FontWeight.W400,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp,
),
bodyMedium = TextStyle(
fontFamily = Roboto,
fontWeight = FontWeight.W400,
fontSize = 14.sp,
lineHeight = 20.sp,
letterSpacing = 0.25.sp,
),
bodySmall = TextStyle(
fontFamily = Roboto,
fontWeight = FontWeight.W400,
fontSize = 12.sp,
lineHeight = 16.sp,
letterSpacing = 0.4.sp,
),
labelMedium = TextStyle(
fontFamily = Roboto,
fontWeight = FontWeight.Medium,
fontSize = 12.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp,
),
labelSmall = TextStyle(
fontFamily = Roboto,
fontWeight = FontWeight.Medium,
fontSize = 11.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp,
),
)*/

View File

@@ -0,0 +1,40 @@
package de.sebse.fuplanner2.ui.tools.previews
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import de.sebse.fuplanner2.database.Announcement
import de.sebse.fuplanner2.database.Attachment
import de.sebse.fuplanner2.utils.Faker
import de.sebse.fuplanner2.utils.getFaker
class AnnouncementPreviewProvider(private val faker: Faker = getFaker()) : PreviewParameterProvider<Announcement> {
override val values = generateSequence { getItem() }
private fun getItem(): Announcement {
val title = faker.strings.title()
return Announcement(
faker.primitive.long(0, 100),
faker.primitive.long(0, 100),
faker.other.lastRefreshed(),
faker.strings.uuid(title),
title,
faker.strings.lorem(100, 1000),
faker.other.date(-20, -2),
"${faker.name.firstName()} ${faker.name.lastName()}",
AttachmentPreviewProvider(faker).values
.take(faker.primitive.int(1, 4))
.toList()
)
}
}
class AttachmentPreviewProvider(private val faker: Faker = getFaker()) : PreviewParameterProvider<Attachment> {
override val values = generateSequence { getItem() }
private fun getItem(): Attachment {
return Attachment(
faker.internet.url(),
faker.strings.title(),
faker.internet.mime()
)
}
}

View File

@@ -1,4 +1,4 @@
package de.sebse.fuplanner2.ui.details
package de.sebse.fuplanner2.ui.tools.previews
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import de.sebse.fuplanner2.database.Course
@@ -7,12 +7,13 @@ import de.sebse.fuplanner2.utils.Faker
import de.sebse.fuplanner2.utils.getFaker
class LecturerPreviewProvider(private val faker: Faker = getFaker()) : PreviewParameterProvider<Lecturer> {
private val resp = faker.primitive.int(1, 2)
override val values = (0..10).map {
getLecturer(it < resp)
}.asSequence()
override val values = sequence {
yield(getItem(true))
yield(getItem(faker.primitive.bool(.5f)))
yieldAll(generateSequence { getItem(false) })
}
private fun getLecturer(isResponsible: Boolean): Lecturer {
private fun getItem(isResponsible: Boolean): Lecturer {
val firstName = faker.name.firstName()
val lastName = faker.name.lastName()
return Lecturer(
@@ -25,17 +26,16 @@ class LecturerPreviewProvider(private val faker: Faker = getFaker()) : PreviewPa
}
class CoursePreviewProvider(private val faker: Faker = getFaker()) : PreviewParameterProvider<Course> {
var isSummer = false
var year = 21
override val values = (0..10).map {
val res = getCourse()
private var isSummer = false
private var year = 21
override val values = sequence {
yield(getItem())
val reduce = faker.primitive.bool(.3f)
year = if (reduce && isSummer) year-1 else year
isSummer = if (reduce) !isSummer else isSummer
res
}.asSequence()
}
private fun getCourse(): Course {
private fun getItem(): Course {
val diff = 1000L*60*60*24*5
val title =
"${faker.strings.title()} ${if (isSummer) "S" else "W"} ${if (isSummer) year else "$year/${year + 1}"}"

View File

@@ -1,4 +1,4 @@
package de.sebse.fuplanner2.viewmodels
package de.sebse.fuplanner2.ui.tools.viewmodels
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel

View File

@@ -1,4 +1,4 @@
package de.sebse.fuplanner2.viewmodels
package de.sebse.fuplanner2.ui.tools.viewmodels
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel

View File

@@ -21,6 +21,9 @@ class Faker(randomSeed: Int) {
val primitive: FakerPrimitive
get() = FakerPrimitive(random)
val other: FakerOther
get() = FakerOther(random)
val strings: FakerStrings
get() = FakerStrings(random)
}
@@ -29,6 +32,7 @@ class FakerName(private val random: Random) {
fun lastName(): String {
return LASTNAMES.random(random)
}
fun firstName(): String {
return FIRSTNAMES.random(random)
}
@@ -60,6 +64,19 @@ class FakerInternet(private val random: Random) {
.replace(Regex("[^a-z0-9.]"), "")
return "$user@fu-berlin.de"
}
fun mime(): String {
return MIME_TYPES.random(random)
}
fun url(): String {
return "https://example.de/file"
}
companion object {
private val MIME_TYPES = listOf("application/pdf", "text/plain", "text/csv",
"application/msword")
}
}
class FakerPrimitive(private val random: Random) {
@@ -76,11 +93,31 @@ class FakerPrimitive(private val random: Random) {
}
}
class FakerOther(private val random: Random) {
fun lastRefreshed(): Long {
return FakerPrimitive(random).long(
System.currentTimeMillis() - DIFF,
System.currentTimeMillis() + DIFF
)
}
fun date(startDay: Int, endDay: Int): Long {
return FakerPrimitive(random).long(
System.currentTimeMillis() + DAY * startDay,
System.currentTimeMillis() + DAY * endDay
)
}
companion object {
const val DAY = 1000L*60*60*24
const val DIFF = DAY*5
}
}
class FakerStrings(private val random: Random) {
fun lorem(min: Int, max: Int): String {
val length = (min..max).random(random)
val loremArray = LOREM.split(" ").size
val n = ceil(length.toFloat() / loremArray).toInt()
val n = ceil(length.toFloat() / loremArray).toInt() + 1
val repeatedArray = ("$LOREM ")
.repeat(n)
.trim()
@@ -109,7 +146,7 @@ class FakerStrings(private val random: Random) {
}
companion object {
val LOREM = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy "+
const val LOREM = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy "+
"eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam "+
"voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita "+
"kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem "+

View File

@@ -12,7 +12,6 @@ import de.sebse.fuplanner2.database.Course
import de.sebse.fuplanner2.database.User
import de.sebse.fuplanner2.utils.Notifications
import de.sebse.fuplanner2.utils.UpdateResult
import de.sebse.fuplanner2.utils.console
import de.sebse.fuplanner2.whiteboard.Whiteboard
import de.sebse.fuplanner2.whiteboard.getCourse
import de.sebse.fuplanner2.whiteboard.getCourses
@@ -39,6 +38,7 @@ class CourseWorker(context: Context, params: WorkerParameters) : AbstractAccount
updates.added.forEach {
if (it.isSummerSemester == latestSemester.semester && it.year == latestSemester.year) {
EventWorker.work(applicationContext, database, user, it)
AnnouncementWorker.work(applicationContext, database, user, it)
}
}
Notifications.courseUpdates(updates, database, applicationContext)

View File

@@ -7,7 +7,6 @@ import de.sebse.fuplanner2.auth.AppAccounts
import de.sebse.fuplanner2.database.AppDatabase
import de.sebse.fuplanner2.database.Course
import de.sebse.fuplanner2.utils.Notifications
import de.sebse.fuplanner2.utils.Updatable
import de.sebse.fuplanner2.utils.UpdateResult
import de.sebse.fuplanner2.utils.mergeUpdatable
@@ -52,6 +51,7 @@ class SyncWorker(context: Context, params: WorkerParameters) : CoroutineWorker(c
courseCreations.forEach { course ->
if (course.isSummerSemester == latestSemester.semester && course.year == latestSemester.year) {
EventWorker.work(applicationContext, database, it, course)
AnnouncementWorker.work(applicationContext, database, it, course)
}
}
Notifications.courseUpdates(notifications, database, applicationContext)

View File

@@ -1,5 +1,6 @@
package de.sebse.fuplanner2
import de.sebse.fuplanner2.ui.tools.previews.AnnouncementPreviewProvider
import de.sebse.fuplanner2.utils.getFaker
import org.junit.Test
@@ -17,4 +18,9 @@ class FakerTest {
getFaker().strings.lorem(100, 1000)
getFaker().strings.lorem(100, 1000)
}
@Test
fun announcement() {
AnnouncementPreviewProvider().values.take(10)
}
}