diff --git a/app/src/main/java/de/sebse/fuplanner2/database/AnnouncementDao.kt b/app/src/main/java/de/sebse/fuplanner2/database/AnnouncementDao.kt index 609b752..bf1e524 100644 --- a/app/src/main/java/de/sebse/fuplanner2/database/AnnouncementDao.kt +++ b/app/src/main/java/de/sebse/fuplanner2/database/AnnouncementDao.kt @@ -6,18 +6,21 @@ import androidx.room.* @Dao interface AnnouncementDao { - @Query("SELECT * FROM announcement WHERE courseId = :courseId ORDER BY createdOn ASC") + @Query("SELECT * FROM announcement WHERE courseId = :courseId ORDER BY createdOn DESC") fun getAll1(courseId: Long): DataSource.Factory - @Query("SELECT * FROM announcement WHERE courseId = :courseId ORDER BY createdOn ASC") + @Query("SELECT * FROM announcement WHERE courseId = :courseId ORDER BY createdOn DESC") fun getAll2(courseId: Long): List - @Query("SELECT * FROM announcement WHERE courseId = :courseId ORDER BY createdOn ASC") + @Query("SELECT * FROM announcement WHERE courseId = :courseId ORDER BY createdOn DESC") fun getAll3(courseId: Long): LiveData> @Query("SELECT * FROM announcement WHERE uid = :announcementId LIMIT 1") fun getAnnouncementById(announcementId: Long): Announcement + @Query("SELECT * FROM announcement WHERE uid = :announcementId LIMIT 1") + fun getAnnouncementById2(announcementId: Long): LiveData + @Insert(onConflict = OnConflictStrategy.IGNORE) fun insert(announcement: Announcement): Long diff --git a/app/src/main/java/de/sebse/fuplanner2/database/Course.kt b/app/src/main/java/de/sebse/fuplanner2/database/Course.kt index 045d8b4..2f933fe 100644 --- a/app/src/main/java/de/sebse/fuplanner2/database/Course.kt +++ b/app/src/main/java/de/sebse/fuplanner2/database/Course.kt @@ -62,7 +62,7 @@ data class Course ( actCtx: Context, type: Notifications.CourseUpdateType, data: JsonObject - ): CharSequence? = when (type) { + ): CharSequence = when (type) { Notifications.CourseUpdateType.REMOVED -> actCtx.getHtmlSpannedString(R.string.not_course_update_course_removed, data.string("title")) Notifications.CourseUpdateType.UPDATED -> actCtx.getHtmlSpannedString(R.string.not_course_update_course_updated, data.string("title")) Notifications.CourseUpdateType.ADDED -> actCtx.getHtmlSpannedString(R.string.not_course_update_course_added, data.string("title")) @@ -72,7 +72,7 @@ data class Course ( actCtx: Context, type: Notifications.CourseUpdateType, data: JsonObject - ): CharSequence? = when (type) { + ): CharSequence = when (type) { Notifications.CourseUpdateType.REMOVED -> actCtx.getHtmlSpannedString(R.string.adapter_course_update_course_removed, data.string("title")) Notifications.CourseUpdateType.UPDATED -> actCtx.getHtmlSpannedString(R.string.adapter_course_update_course_updated, data.string("title")) Notifications.CourseUpdateType.ADDED -> actCtx.getHtmlSpannedString(R.string.adapter_course_update_course_added, data.string("title")) diff --git a/app/src/main/java/de/sebse/fuplanner2/mainActivityComponents.kt b/app/src/main/java/de/sebse/fuplanner2/mainActivityComponents.kt index 48a63e4..82b3889 100644 --- a/app/src/main/java/de/sebse/fuplanner2/mainActivityComponents.kt +++ b/app/src/main/java/de/sebse/fuplanner2/mainActivityComponents.kt @@ -7,6 +7,8 @@ import androidx.navigation.compose.composable import androidx.navigation.navArgument import de.sebse.fuplanner2.ui.courses.CoursesScreen import de.sebse.fuplanner2.ui.details.CourseDetailsScreen +import de.sebse.fuplanner2.ui.details_announcements.CourseAnnouncementScreen +import de.sebse.fuplanner2.ui.details_description.CourseDescriptionScreen sealed class MenuItem { object Courses : NavLinks(R.string.menu_courses, R.drawable.ic_menu_courses, "courses") @@ -42,5 +44,25 @@ fun MainActivityComposable() { val id = it.arguments!!.getLong("id") CourseDetailsScreen(tools, id) } + composable( + arguments = listOf( + navArgument("id") { type = NavType.LongType } + ), + route = "${MenuItem.Courses.route}/{id}/description" + ) { + val id = it.arguments!!.getLong("id") + CourseDescriptionScreen(tools, id) + } + composable( + arguments = listOf( + navArgument("id") { type = NavType.LongType }, + navArgument("announceId") { type = NavType.LongType } + ), + route = "${MenuItem.Courses.route}/{id}/announcements/{announceId}" + ) { + val id = it.arguments!!.getLong("id") + val announceId = it.arguments!!.getLong("announceId") + CourseAnnouncementScreen(tools, id, announceId) + } } } diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/courses/CoursesScreen.kt b/app/src/main/java/de/sebse/fuplanner2/ui/courses/CoursesScreen.kt index 0c16660..4cccb30 100644 --- a/app/src/main/java/de/sebse/fuplanner2/ui/courses/CoursesScreen.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/courses/CoursesScreen.kt @@ -1,7 +1,6 @@ package de.sebse.fuplanner2.ui.courses import android.content.res.Configuration -import android.util.Log import androidx.annotation.StringRes import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background @@ -57,7 +56,6 @@ fun CoursesScreen(tools: Tools) { AppTheme { GroupedCourseList(groups = groups) { course -> course.uid?.let { - Log.d("WHERE TO GO", "${MenuItem.Courses.route}/$it") tools.navTo("${MenuItem.Courses.route}/$it") } } @@ -133,9 +131,11 @@ fun CourseItem(id: Long?, title: String, lecturers: String, type: String, onclic Column( modifier = Modifier .height(1.5.dp) - .background(Brush.horizontalGradient( - colors = listOf(color, MaterialTheme.colors.surface) - )) + .background( + Brush.horizontalGradient( + colors = listOf(color, MaterialTheme.colors.surface) + ) + ) ) {} Column( modifier = Modifier diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/details/CourseDetailsScreen.kt b/app/src/main/java/de/sebse/fuplanner2/ui/details/CourseDetailsScreen.kt index 404db36..add8056 100644 --- a/app/src/main/java/de/sebse/fuplanner2/ui/details/CourseDetailsScreen.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/details/CourseDetailsScreen.kt @@ -11,17 +11,22 @@ 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 de.sebse.fuplanner2.MenuItem import de.sebse.fuplanner2.R import de.sebse.fuplanner2.Tools import de.sebse.fuplanner2.database.Announcement import de.sebse.fuplanner2.database.Course +import de.sebse.fuplanner2.database.Event +import de.sebse.fuplanner2.database.Lecturer import de.sebse.fuplanner2.ui.details.components.AnnouncementItem +import de.sebse.fuplanner2.ui.details.components.EventItem import de.sebse.fuplanner2.ui.details.components.LecturerItem import de.sebse.fuplanner2.ui.details.components.QuickLinks import de.sebse.fuplanner2.ui.shared.Heading 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.previews.EventPreviewProvider import de.sebse.fuplanner2.ui.tools.viewmodels.DetailsViewModel import de.sebse.fuplanner2.ui.tools.viewmodels.DetailsViewModelFactory import kotlin.math.min @@ -31,6 +36,7 @@ fun CourseDetailsScreen(tools: Tools, id: Long) { val coursesViewModel: DetailsViewModel = viewModel(factory = DetailsViewModelFactory(id)) val course by coursesViewModel.course.observeAsState() val announcements by coursesViewModel.announcements.observeAsState() + val events by coursesViewModel.events.observeAsState() val title = course?.title val context = LocalContext.current LaunchedEffect(title) { @@ -39,25 +45,47 @@ fun CourseDetailsScreen(tools: Tools, id: Long) { LaunchedEffect(true) { coursesViewModel.refresh(context) } - CourseDetailsScreen(course, announcements, id) + CourseDetailsScreen( + course?.lecturers ?: emptyList(), + announcements ?: emptyList(), + events ?: emptyList(), + id, + course?.title ?: "" + ) { + tools.navTo(it) + } } @Composable -fun CourseDetailsScreen(course: Course?, announcement: List?, id: Long) { - val announcements = announcement?.subList(0, min(announcement.size, 3)) ?: listOf() +fun CourseDetailsScreen( + lecturers: List, + announcements: List, + events: List, + id: Long, + title: String, + onClick: (String) -> Unit +) { LazyColumn { item { - QuickLinks(courseId = id) - Heading(stringResource(R.string.lecturers)) + QuickLinks(courseId = id, onClick) + if (lecturers.isNotEmpty()) Heading(stringResource(R.string.lecturers)) } - items(course?.lecturers ?: listOf()) { - LecturerItem(lecturer = it, courseTitle = course?.title ?: "") + items(lecturers.sortedBy { (if (it.isResponsible) "AAAA" else "ZZZZ") + it.lastName }) { + LecturerItem(lecturer = it, courseTitle = title) } item { - Heading(stringResource(R.string.announcements)) + if (announcements.isNotEmpty()) Heading(stringResource(R.string.announcements)) } - items(items = announcements) { - AnnouncementItem(it) + items(items = announcements.subList(0, min(announcements.size, 3)) ?: listOf()) { + AnnouncementItem(it) { + onClick("${MenuItem.Courses.route}/$id/announcements/${it.uid}") + } + } + item { + if (events.isNotEmpty()) Heading(stringResource(R.string.events)) + } + items(items = events.subList(0, min(events.size, 3))) { + EventItem(it) } // TODO: Add current assignments, upcoming events } @@ -67,6 +95,12 @@ fun CourseDetailsScreen(course: Course?, announcement: List?, id: @Composable fun CourseDetailsScreenPreview(@PreviewParameter(CoursePreviewProvider::class, 1) course: Course) { AppTheme { - CourseDetailsScreen(course, AnnouncementPreviewProvider().values.take(3).toList(), course.uid!!) + CourseDetailsScreen( + course.lecturers, + AnnouncementPreviewProvider().values.take(3).toList(), + EventPreviewProvider().values.take(3).toList(), + course.uid!!, + course.title + ) { } } } diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/details/components/AnnouncementItem.kt b/app/src/main/java/de/sebse/fuplanner2/ui/details/components/AnnouncementItem.kt index e671271..c4398d1 100644 --- a/app/src/main/java/de/sebse/fuplanner2/ui/details/components/AnnouncementItem.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/details/components/AnnouncementItem.kt @@ -26,7 +26,7 @@ fun AnnouncementItem(announcement: Announcement, click: () -> Unit) { .clickable(true, onClick = click) ) { Text( - text = announcement.title ?: "Title", + text = announcement.title, style = MaterialTheme.typography.h6 ) Text( diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/details/components/EventItem.kt b/app/src/main/java/de/sebse/fuplanner2/ui/details/components/EventItem.kt new file mode 100644 index 0000000..fc04ae3 --- /dev/null +++ b/app/src/main/java/de/sebse/fuplanner2/ui/details/components/EventItem.kt @@ -0,0 +1,45 @@ +package de.sebse.fuplanner2.ui.details.components + +import androidx.compose.foundation.clickable +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.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import de.sebse.fuplanner2.database.Event +import de.sebse.fuplanner2.ui.shared.FuCardColumn +import de.sebse.fuplanner2.ui.theme.AppTheme +import de.sebse.fuplanner2.ui.tools.previews.EventPreviewProvider +import de.sebse.fuplanner2.utils.toDateTimeString + +@Composable +fun EventItem(event: Event) { + EventItem(event) { } +} + +@Composable +fun EventItem(event: Event, click: () -> Unit) { + FuCardColumn( + modifier = Modifier + .clickable(true, onClick = click) + ) { + Text( + text = event.title, + style = MaterialTheme.typography.h6 + ) + Text( + text = event.startDateTime.toDateTimeString(LocalContext.current) ?: "", + style = MaterialTheme.typography.subtitle1 + ) + } +} + +@Preview +@Composable +fun EventItemPreview(@PreviewParameter(EventPreviewProvider::class, 5) event: Event) { + AppTheme { + EventItem(event) + } +} diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/details/components/QuickLinks.kt b/app/src/main/java/de/sebse/fuplanner2/ui/details/components/QuickLinks.kt index ab1235e..5025e84 100644 --- a/app/src/main/java/de/sebse/fuplanner2/ui/details/components/QuickLinks.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/details/components/QuickLinks.kt @@ -2,6 +2,7 @@ package de.sebse.fuplanner2.ui.details.components import androidx.annotation.StringRes import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.GridCells @@ -24,7 +25,7 @@ data class QuickLinkProps(@StringRes val name: Int, val route: String) @OptIn(ExperimentalFoundationApi::class) @Composable -fun QuickLinks(courseId: Long) { +fun QuickLinks(courseId: Long, onClick: (String) -> Unit) { val list = listOf( QuickLinkProps(R.string.description, "courses/$courseId/description"), QuickLinkProps(R.string.resources, "courses/$courseId/resources"), @@ -40,7 +41,8 @@ fun QuickLinks(courseId: Long) { backgroundColor = MaterialTheme.colors.secondary, modifier = Modifier .padding(dimensionResource(R.dimen.card_view_margin)) - .fillMaxWidth(), + .fillMaxWidth() + .clickable { onClick(it.route) }, elevation = dimensionResource(R.dimen.card_view_elevation), ) { Text( @@ -60,6 +62,6 @@ fun QuickLinks(courseId: Long) { @Composable fun QuickLinksPreview() { AppTheme { - QuickLinks(3) + QuickLinks(3) { } } } diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/details_announcements/CourseAnnouncementScreen.kt b/app/src/main/java/de/sebse/fuplanner2/ui/details_announcements/CourseAnnouncementScreen.kt new file mode 100644 index 0000000..37a682b --- /dev/null +++ b/app/src/main/java/de/sebse/fuplanner2/ui/details_announcements/CourseAnnouncementScreen.kt @@ -0,0 +1,66 @@ +package de.sebse.fuplanner2.ui.details_announcements + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.ui.Modifier +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 de.sebse.fuplanner2.R +import de.sebse.fuplanner2.Tools +import de.sebse.fuplanner2.database.Announcement +import de.sebse.fuplanner2.database.Course +import de.sebse.fuplanner2.ui.shared.Heading +import de.sebse.fuplanner2.ui.shared.HtmlText +import de.sebse.fuplanner2.ui.theme.AppTheme +import de.sebse.fuplanner2.ui.tools.previews.CoursePreviewProvider +import de.sebse.fuplanner2.ui.tools.viewmodels.AnnouncementViewModel +import de.sebse.fuplanner2.ui.tools.viewmodels.AnnouncementViewModelFactory +import de.sebse.fuplanner2.ui.tools.viewmodels.DetailsViewModel +import de.sebse.fuplanner2.ui.tools.viewmodels.DetailsViewModelFactory + +@Composable +fun CourseAnnouncementScreen(tools: Tools, id: Long, announcementId: Long) { + val coursesViewModel: DetailsViewModel = + viewModel(factory = DetailsViewModelFactory(id)) + val announcementViewModel: AnnouncementViewModel = + viewModel(factory = AnnouncementViewModelFactory(announcementId)) + val course by coursesViewModel.course.observeAsState() + val announcement by announcementViewModel.observeAsState() + val title = course?.title ?: stringResource(id = R.string.description) + LaunchedEffect(title) { + tools.setTitle(title) + } + announcement?.let { CourseAnnouncementScreen(it) } +} + +@Composable +fun CourseAnnouncementScreen(announcement: Announcement) { + val scrollState = rememberScrollState() + Column( + modifier = Modifier.verticalScroll(scrollState) + ) { + Heading(text = announcement.title) + HtmlText( + html = announcement.body, + modifier = Modifier.fillMaxWidth() + ) + } +} + +@Preview +@Composable +fun CourseAnnouncementScreenPreview(@PreviewParameter(CoursePreviewProvider::class, 1) course: Course) { + AppTheme { + /*CourseAnnouncementScreen( + "scripfsdfsfg
tion" + )*/ + } +} diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/details_description/CourseDescriptionScreen.kt b/app/src/main/java/de/sebse/fuplanner2/ui/details_description/CourseDescriptionScreen.kt new file mode 100644 index 0000000..8f05caf --- /dev/null +++ b/app/src/main/java/de/sebse/fuplanner2/ui/details_description/CourseDescriptionScreen.kt @@ -0,0 +1,59 @@ +package de.sebse.fuplanner2.ui.details_description + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.ui.Modifier +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 de.sebse.fuplanner2.R +import de.sebse.fuplanner2.Tools +import de.sebse.fuplanner2.database.Course +import de.sebse.fuplanner2.ui.shared.Heading +import de.sebse.fuplanner2.ui.shared.HtmlText +import de.sebse.fuplanner2.ui.theme.AppTheme +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 CourseDescriptionScreen(tools: Tools, id: Long) { + val coursesViewModel: DetailsViewModel = viewModel(factory = DetailsViewModelFactory(id)) + val course by coursesViewModel.course.observeAsState() + val title = course?.title ?: stringResource(id = R.string.description) + LaunchedEffect(title) { + tools.setTitle(title) + } + CourseDescriptionScreen(course?.description ?: "") +} + +@Composable +fun CourseDescriptionScreen(description: String) { + val scrollState = rememberScrollState() + Column( + modifier = Modifier.verticalScroll(scrollState) + ) { + Heading(text = stringResource(id = R.string.description)) + HtmlText( + html = description, + modifier = Modifier.fillMaxWidth() + ) + } +} + +@Preview +@Composable +fun CourseDescriptionScreenPreview(@PreviewParameter(CoursePreviewProvider::class, 1) course: Course) { + AppTheme { + CourseDescriptionScreen( + "scripfsdfsfg
tion" + ) + } +} diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/details_description/DescriptionFragment.kt b/app/src/main/java/de/sebse/fuplanner2/ui/details_description/DescriptionFragment.kt deleted file mode 100644 index 3b08047..0000000 --- a/app/src/main/java/de/sebse/fuplanner2/ui/details_description/DescriptionFragment.kt +++ /dev/null @@ -1,45 +0,0 @@ -package de.sebse.fuplanner2.ui.details_description - -import android.content.res.Configuration -import android.os.Build -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.webkit.WebSettings -import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels -import androidx.lifecycle.Observer -import androidx.navigation.NavController -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.ui.tools.viewmodels.DetailsViewModel -import de.sebse.fuplanner2.ui.tools.viewmodels.DetailsViewModelFactory - - -class DescriptionFragment : Fragment() { - - private var title: String = "" - private val args: DescriptionFragmentArgs by navArgs() - private val detailsViewModel: DetailsViewModel by viewModels { DetailsViewModelFactory(args.courseId) } - private lateinit var navController: NavController - private lateinit var binding: FragmentDescriptionBinding - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_description, container, false).apply { - val nightModeFlags = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK - if (nightModeFlags == Configuration.UI_MODE_NIGHT_YES && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - binding.description.settings.forceDark = WebSettings.FORCE_DARK_ON - } - detailsViewModel.course.observe(viewLifecycleOwner, Observer { - binding.description.loadDataWithBaseURL("", it.description, "text/html", "UTF-8", "") - }) - navController = findNavController() - } - } -} diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/shared/Heading.kt b/app/src/main/java/de/sebse/fuplanner2/ui/shared/Heading.kt index a2b9723..85401f5 100644 --- a/app/src/main/java/de/sebse/fuplanner2/ui/shared/Heading.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/shared/Heading.kt @@ -1,28 +1,48 @@ package de.sebse.fuplanner2.ui.shared +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.tooling.preview.Preview import de.sebse.fuplanner2.R import de.sebse.fuplanner2.ui.theme.AppTheme @Composable -fun Heading(text: String) { - Text( - text = text, - style = MaterialTheme.typography.h5, - modifier = Modifier.padding(top = dimensionResource(id = R.dimen.header_padding)) - ) +fun Heading(text: String, onClick: (() -> Unit)? = null) { + Box( + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = text, + style = MaterialTheme.typography.h5, + modifier = Modifier + .padding(top = dimensionResource(id = R.dimen.header_padding)) + ) + if (onClick != null) Text( + text = "More >>", + style = MaterialTheme.typography.subtitle2, + textDecoration = TextDecoration.Underline, + color = MaterialTheme.colors.primary, + modifier = Modifier + .padding(top = dimensionResource(id = R.dimen.header_padding)) + .align(Alignment.BottomEnd) + .clickable(onClick = onClick) + ) + } } @Preview @Composable fun HeadingPreview() { AppTheme { - Text("Super Cool") + Heading("Super cool") { } } } diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/shared/HtmlText.kt b/app/src/main/java/de/sebse/fuplanner2/ui/shared/HtmlText.kt new file mode 100644 index 0000000..1c85cb0 --- /dev/null +++ b/app/src/main/java/de/sebse/fuplanner2/ui/shared/HtmlText.kt @@ -0,0 +1,21 @@ +package de.sebse.fuplanner2.ui.shared + +import android.text.method.LinkMovementMethod +import android.widget.TextView +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.viewinterop.AndroidView +import androidx.core.text.HtmlCompat + +@Composable +fun HtmlText(html: String, modifier: Modifier = Modifier) { + AndroidView( + modifier = modifier, + factory = { context -> TextView(context) }, + update = { + it.text = HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_COMPACT) + it.movementMethod = LinkMovementMethod.getInstance() + it.setTextIsSelectable(true) + } + ) +} diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/tools/previews/event.kt b/app/src/main/java/de/sebse/fuplanner2/ui/tools/previews/event.kt new file mode 100644 index 0000000..49c5ebb --- /dev/null +++ b/app/src/main/java/de/sebse/fuplanner2/ui/tools/previews/event.kt @@ -0,0 +1,34 @@ +package de.sebse.fuplanner2.ui.tools.previews + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import de.sebse.fuplanner2.database.Event +import de.sebse.fuplanner2.utils.Faker +import de.sebse.fuplanner2.utils.getFaker +import java.util.* + +class EventPreviewProvider(private val faker: Faker = getFaker()) : PreviewParameterProvider { + val title = faker.strings.title() + override val values = generateSequence(0) { it + 1 } + .map { getItem(it) } + + private fun getItem(num: Int): Event { + val isExam = num == 20 + val cal = Calendar.getInstance() + cal.add(Calendar.DATE, num / 2 * 7 + num % 2 * 3) + cal.set(Calendar.HOUR, 14 + (num % 2) * 2) + cal.set(Calendar.MINUTE, 30) + cal.set(Calendar.SECOND, 0) + cal.set(Calendar.MILLISECOND, 0) + + return Event( + faker.primitive.long(0, 100), + faker.primitive.long(0, 100), + faker.other.lastRefreshed(), + title, + 90*60*1000 + (if (isExam) 1L else 0), + cal.timeInMillis, + "Hörsaal T9", + if (isExam) "Klausur" else "Vorlesung", + ) + } +} diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/tools/viewmodels/AnnouncementViewModel.kt b/app/src/main/java/de/sebse/fuplanner2/ui/tools/viewmodels/AnnouncementViewModel.kt new file mode 100644 index 0000000..d447de7 --- /dev/null +++ b/app/src/main/java/de/sebse/fuplanner2/ui/tools/viewmodels/AnnouncementViewModel.kt @@ -0,0 +1,25 @@ +package de.sebse.fuplanner2.ui.tools.viewmodels + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.livedata.observeAsState +import androidx.lifecycle.LiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import de.sebse.fuplanner2.database.Announcement +import de.sebse.fuplanner2.database.AppDatabase + + +class AnnouncementViewModelFactory(private val announcementId: Long): ViewModelProvider.NewInstanceFactory() { + override fun create(modelClass: Class): T = AnnouncementViewModel(announcementId) as T +} + +class AnnouncementViewModel(private val announcementId: Long) : ViewModel() { + private val announcement: LiveData = + AppDatabase.getInstance().announcementDao().getAnnouncementById2(announcementId) + + @Composable + fun observeAsState(): State { + return announcement.observeAsState() + } +} diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/tools/viewmodels/DetailsViewModel.kt b/app/src/main/java/de/sebse/fuplanner2/ui/tools/viewmodels/DetailsViewModel.kt index 71f09ac..57be566 100644 --- a/app/src/main/java/de/sebse/fuplanner2/ui/tools/viewmodels/DetailsViewModel.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/tools/viewmodels/DetailsViewModel.kt @@ -11,6 +11,7 @@ import de.sebse.fuplanner2.auth.AppAccounts import de.sebse.fuplanner2.database.Announcement import de.sebse.fuplanner2.database.AppDatabase import de.sebse.fuplanner2.database.Course +import de.sebse.fuplanner2.database.Event import de.sebse.fuplanner2.utils.enqueueOneTimeWork import de.sebse.fuplanner2.worker.AbstractAccountWorker import de.sebse.fuplanner2.worker.CourseWorker @@ -37,4 +38,8 @@ class DetailsViewModel(private val courseId: Long) : ViewModel() { AppDatabase.getInstance().announcementDao().getAll1(courseId), 50 ).build() + val events: LiveData> = LivePagedListBuilder( + AppDatabase.getInstance().eventDao().getAll1(courseId), + 50 + ).build() } diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index b5ac607..3956e5b 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -39,17 +39,6 @@ android:name="title" app:argType="string" /> - - - - 4dp 5dp 4dp - 8dp + 16dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 661f43f..5db4089 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -62,6 +62,7 @@ Course Type Description Resources + Announcement One course message %1$d course messages