This commit is contained in:
Sebastian Seedorf
2021-11-14 22:58:36 +01:00
parent def91f28a2
commit 8302884fdc
16 changed files with 72 additions and 290 deletions

View File

@@ -9,7 +9,6 @@ import androidx.room.Index
import androidx.room.PrimaryKey
import com.beust.klaxon.JsonObject
import de.sebse.fuplanner2.R
import de.sebse.fuplanner2.ui.notification.NotificationFragmentDirections
import de.sebse.fuplanner2.utils.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
@@ -82,7 +81,7 @@ data class Announcement (
?.let { AppDatabase.getInstance().courseDao().getCourseById2(it.courseId) }
?.let {
withContext(Dispatchers.Main) {
navController.navigate(NotificationFragmentDirections.actionNavNotificationsToCourseDetails(it.uid!!, it.title))
//navController.navigate(NotificationFragmentDirections.actionNavNotificationsToCourseDetails(it.uid!!, it.title))
}
}
}

View File

@@ -9,7 +9,6 @@ import androidx.room.Index
import androidx.room.PrimaryKey
import com.beust.klaxon.JsonObject
import de.sebse.fuplanner2.R
import de.sebse.fuplanner2.ui.notification.NotificationFragmentDirections
import de.sebse.fuplanner2.utils.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
@@ -91,7 +90,7 @@ data class Course (
?.let { AppDatabase.getInstance().courseDao().getCourseById2(it) }
?.let {
withContext(Dispatchers.Main) {
navController.navigate(NotificationFragmentDirections.actionNavNotificationsToCourseDetails(it.uid!!, it.title))
//navController.navigate(NotificationFragmentDirections.actionNavNotificationsToCourseDetails(it.uid!!, it.title))
}
}
}

View File

@@ -9,6 +9,7 @@ interface CourseDao {
fun getAll(): LiveData<List<Course>>
@Query("SELECT * FROM course INNER JOIN (SELECT year, CASE WHEN isSummerSemester THEN 1 ELSE 0 END AS semester FROM course GROUP BY year, isSummerSemester ORDER BY year DESC, semester ASC LIMIT 1) current WHERE current.year = course.year AND current.semester = CASE WHEN course.isSummerSemester THEN 1 ELSE 0 END ORDER BY title ASC")
@RewriteQueriesToDropUnusedColumns
fun getLatestSemester(): LiveData<List<Course>>
@Query("SELECT year, CASE WHEN isSummerSemester THEN 1 ELSE 0 END AS semester FROM course GROUP BY year, isSummerSemester ORDER BY year DESC, semester ASC LIMIT 1")
@@ -53,4 +54,4 @@ interface CourseDao {
fun delete(courses: List<Course>)
}
data class Semester(val year: Int, val semester: Boolean)
data class Semester(val year: Int, val semester: Boolean)

View File

@@ -9,7 +9,6 @@ import androidx.room.Index
import androidx.room.PrimaryKey
import com.beust.klaxon.JsonObject
import de.sebse.fuplanner2.R
import de.sebse.fuplanner2.ui.notification.NotificationFragmentDirections
import de.sebse.fuplanner2.utils.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
@@ -81,7 +80,7 @@ data class Event (
?.let { AppDatabase.getInstance().courseDao().getCourseById2(it.courseId) }
?.let {
withContext(Dispatchers.Main) {
navController.navigate(NotificationFragmentDirections.actionNavNotificationsToCourseDetails(it.uid!!, it.title))
//navController.navigate(NotificationFragmentDirections.actionNavNotificationsToCourseDetails(it.uid!!, it.title))
}
}
}

View File

@@ -6,7 +6,7 @@ import androidx.navigation.NavType
import androidx.navigation.compose.composable
import androidx.navigation.navArgument
import de.sebse.fuplanner2.ui.courses.CoursesScreen
import de.sebse.fuplanner2.ui.details.components.CourseDetailsScreen
import de.sebse.fuplanner2.ui.details.CourseDetailsScreen
sealed class MenuItem {
object Courses : NavLinks(R.string.menu_courses, R.drawable.ic_menu_courses, "courses")

View File

@@ -38,6 +38,7 @@ import de.sebse.fuplanner2.Tools
import de.sebse.fuplanner2.database.Course
import de.sebse.fuplanner2.ui.details.CoursePreviewProvider
import de.sebse.fuplanner2.utils.color.getColor
import de.sebse.fuplanner2.viewmodels.CoursesViewModel
@Composable
fun CoursesScreen(tools: Tools) {

View File

@@ -0,0 +1,57 @@
package de.sebse.fuplanner2.ui.details
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.livedata.observeAsState
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.Course
import de.sebse.fuplanner2.ui.details.components.QuickLinks
import de.sebse.fuplanner2.viewmodels.DetailsViewModel
import de.sebse.fuplanner2.viewmodels.DetailsViewModelFactory
@Composable
fun CourseDetailsScreen(tools: Tools, id: Long) {
val coursesViewModel: DetailsViewModel = viewModel(factory = DetailsViewModelFactory(id))
val state = coursesViewModel.course.observeAsState()
val title = state.value?.title
LaunchedEffect(title) {
title?.let { tools.setTitle(it) }
}
CourseDetailsScreen(state.value, id)
}
@Composable
fun CourseDetailsScreen(course: Course?, id: Long) {
Column {
QuickLinks(courseId = id)
Text(
text = stringResource(R.string.lecturers),
style = MaterialTheme.typography.h5
)
LazyColumn {
items(course?.lecturers ?: listOf()) {
LecturerItem(lecturer = it, courseTitle = course?.title ?: "")
}
}
// TODO: Add latest announcements, current assignments, upcoming events
}
}
@Preview
@Composable
fun CourseDetailsScreenPreview(@PreviewParameter(CoursePreviewProvider::class, 1) course: Course) {
MdcTheme {
CourseDetailsScreen(course, course.uid!!)
}
}

View File

@@ -1,84 +0,0 @@
package de.sebse.fuplanner2.ui.details
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import de.sebse.fuplanner2.R
import de.sebse.fuplanner2.database.Lecturer
import de.sebse.fuplanner2.ui.*
import de.sebse.fuplanner2.utils.cast
import java.util.*
class DetailsAdapter(private val onQuickLink: (ButtonTypes) -> Unit, private val onMailTo: (Lecturer) -> Unit) : RecyclerView.Adapter<CustomHolder>() {
enum class HeaderTypes {
BUTTONS, LECTURER, ANNOUNCEMENTS, ASSIGNMENTS, EVENTS;
}
enum class ButtonTypes {
DESCRIPTION, RESOURCES, GRADEBOOK, ANNOUNCEMENTS, ASSIGNMENTS, EVENTS;
}
private val positionalData: ArrayList<Any> = arrayListOf()
var lecturers: List<Lecturer> = listOf()
set(value) {
field = value
updatePositionalData()
}
private fun updatePositionalData() {
positionalData.clear()
positionalData.add(HeaderTypes.BUTTONS)
positionalData.add(ViewHolderGenerator.HolderType.BUTTONS)
positionalData.add(HeaderTypes.LECTURER)
positionalData.addAll(lecturers.sortedBy { it.lastName }.sortedBy { !it.isResponsible })
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomHolder {
return ViewHolderGenerator.getHolderByType(parent, viewType)
}
override fun onBindViewHolder(holder: CustomHolder, position: Int) {
// val viewType = getItemViewType(position)
val res = holder.itemView.resources
when (holder) {
is CaptionHolder -> cast<HeaderTypes>(positionalData[position])?.let { type ->
holder.string.text = when (type) {
HeaderTypes.BUTTONS -> res.getString(R.string.quick_links)
HeaderTypes.LECTURER -> res.getString(R.string.lecturers)
HeaderTypes.ANNOUNCEMENTS -> res.getString(R.string.announcements)
HeaderTypes.ASSIGNMENTS -> res.getString(R.string.assignments)
HeaderTypes.EVENTS -> res.getString(R.string.events)
}
}
is QuickLinksHolder -> {
holder.btnAnnouncements.setOnClickListener { this.onQuickLink(ButtonTypes.ANNOUNCEMENTS) }
holder.btnAssignments.setOnClickListener { this.onQuickLink(ButtonTypes.ASSIGNMENTS) }
holder.btnDescription.setOnClickListener { this.onQuickLink(ButtonTypes.DESCRIPTION) }
holder.btnEvents.setOnClickListener { this.onQuickLink(ButtonTypes.EVENTS) }
holder.btnGradebook.setOnClickListener { this.onQuickLink(ButtonTypes.GRADEBOOK) }
holder.btnResources.setOnClickListener { this.onQuickLink(ButtonTypes.RESOURCES) }
}
is MailHolder -> cast<Lecturer>(positionalData[position])?.let { type ->
holder.title.text = res.getString(R.string.full_name, type.firstName, type.lastName)
holder.subLeft.text = type.email
holder.itemView.setOnClickListener { this.onMailTo(type) }
}
is ListItemHolder -> when (positionalData[position]) {
}
}
}
override fun getItemViewType(position: Int): Int {
return when (positionalData[position]) {
is HeaderTypes -> ViewHolderGenerator.HolderType.HEADER.ordinal
ViewHolderGenerator.HolderType.BUTTONS -> ViewHolderGenerator.HolderType.BUTTONS.ordinal
is Lecturer -> ViewHolderGenerator.HolderType.MAIL.ordinal
else -> ViewHolderGenerator.HolderType.ITEM.ordinal
}
}
// Return the size of your dataset (invoked by the layout manager)
override fun getItemCount() = positionalData.size
}

View File

@@ -1,98 +0,0 @@
package de.sebse.fuplanner2.ui.details
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.Column
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.res.stringResource
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.NavController
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.google.android.material.composethemeadapter.MdcTheme
import de.sebse.fuplanner2.R
import de.sebse.fuplanner2.database.Lecturer
class DetailsFragment : Fragment() {
private val args: DetailsFragmentArgs by navArgs()
private val detailsViewModel: DetailsViewModel by viewModels { DetailsViewModelFactory(args.courseId) }
private lateinit var navController: NavController
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
navController = findNavController()
return ComposeView(requireContext()).apply {
setContent {
val course = detailsViewModel.course.observeAsState(null).value
(activity as? AppCompatActivity)?.supportActionBar?.title = args.title
MdcTheme {
Column {
Text(
text = stringResource(R.string.description),
style = MaterialTheme.typography.h5
)
}
}
}
}
}
private fun sendMail(lecturer: Lecturer) {
val intent = Intent(Intent.ACTION_SENDTO)
intent.type = "text/html"
intent.data = Uri.fromParts("mailto", lecturer.email, null)
intent.putExtra(Intent.EXTRA_SUBJECT, args.title)
intent.putExtra(
Intent.EXTRA_TEXT,
getString(R.string.email_preview, lecturer.firstName, lecturer.lastName)
)
startActivity(
Intent.createChooser(
intent,
getString(R.string.send_email, lecturer.firstName, lecturer.lastName)
)
)
}
private fun launchFragment(btnType: DetailsAdapter.ButtonTypes) {
when (btnType) {
DetailsAdapter.ButtonTypes.DESCRIPTION ->
this.navController.navigate(
DetailsFragmentDirections.actionCourseDetailsToDescriptionFragment(
args.courseId,
args.title
)
)
DetailsAdapter.ButtonTypes.RESOURCES -> TODO()
DetailsAdapter.ButtonTypes.GRADEBOOK -> TODO()
DetailsAdapter.ButtonTypes.ANNOUNCEMENTS ->
this.navController.navigate(
DetailsFragmentDirections.actionCourseDetailsToCourseAnnouncements(
args.courseId,
args.title
)
)
DetailsAdapter.ButtonTypes.ASSIGNMENTS -> TODO()
DetailsAdapter.ButtonTypes.EVENTS ->
this.navController.navigate(
DetailsFragmentDirections.actionCourseDetailsToCourseEvents(
args.courseId,
args.title
)
)
}
}
}

View File

@@ -2,65 +2,25 @@ package de.sebse.fuplanner2.ui.details.components
import androidx.annotation.StringRes
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.GridCells
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyVerticalGrid
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
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.Course
import de.sebse.fuplanner2.ui.details.CoursePreviewProvider
import de.sebse.fuplanner2.ui.details.DetailsViewModel
import de.sebse.fuplanner2.ui.details.DetailsViewModelFactory
import de.sebse.fuplanner2.ui.details.LecturerItem
@Composable
fun CourseDetailsScreen(tools: Tools, id: Long) {
val coursesViewModel: DetailsViewModel = viewModel(factory = DetailsViewModelFactory(id))
val state = coursesViewModel.course.observeAsState()
val title = state.value?.title
LaunchedEffect(title) {
title?.let { tools.setTitle(it) }
}
CourseDetailsScreen(state.value, id)
}
@Composable
fun CourseDetailsScreen(course: Course?, id: Long) {
Column {
QuickLinks(courseId = id)
Text(
text = stringResource(R.string.lecturers),
style = MaterialTheme.typography.h5
)
LazyColumn {
items(course?.lecturers ?: listOf()) {
LecturerItem(lecturer = it, courseTitle = course?.title ?: "")
}
}
}
}
data class QuickLinkProps(@StringRes val name: Int, val route: String)
@OptIn(ExperimentalFoundationApi::class)
@@ -101,14 +61,6 @@ fun QuickLinks(courseId: Long) {
}
}
@Preview
@Composable
fun CourseDetailsScreenPreview(@PreviewParameter(CoursePreviewProvider::class, 1) course: Course) {
MdcTheme {
CourseDetailsScreen(course, course.uid!!)
}
}
@Preview
@Composable
fun QuickLinksPreview() {

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.ui.details.DetailsViewModel
import de.sebse.fuplanner2.ui.details.DetailsViewModelFactory
import de.sebse.fuplanner2.viewmodels.DetailsViewModel
import de.sebse.fuplanner2.viewmodels.DetailsViewModelFactory
class DescriptionFragment : Fragment() {

View File

@@ -53,7 +53,7 @@ class ScheduleFragment : Fragment() {
}
.setPositiveButton(R.string.view_course) { _, _ ->
alertCourse?.let { course ->
navController.navigate(ScheduleFragmentDirections.actionNavScheduleToCourseDetails(course.uid!!, course.title))
//navController.navigate(ScheduleFragmentDirections.actionNavScheduleToCourseDetails(course.uid!!, course.title))
}
}
.create()

View File

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

View File

@@ -1,4 +1,4 @@
package de.sebse.fuplanner2.ui.details
package de.sebse.fuplanner2.viewmodels
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
@@ -13,4 +13,4 @@ class DetailsViewModelFactory(private val courseId: Long): ViewModelProvider.New
class DetailsViewModel(courseId: Long) : ViewModel() {
val course: LiveData<Course> = AppDatabase.getInstance().courseDao().getCourseById(courseId)
}
}

View File

@@ -14,46 +14,7 @@
android:id="@+id/nav_schedule"
android:name="de.sebse.fuplanner2.ui.schedule.ScheduleFragment"
android:label="@string/menu_schedule"
tools:layout="@layout/fragment_schedule">
<action
android:id="@+id/action_nav_schedule_to_course_details"
app:destination="@id/course_details"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
</fragment>
<fragment
android:id="@+id/course_details"
android:name="de.sebse.fuplanner2.ui.details.DetailsFragment">
<action
android:id="@+id/action_course_details_to_descriptionFragment"
app:destination="@id/course_description"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
<action
android:id="@+id/action_course_details_to_course_events"
app:destination="@id/course_events"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
<action
android:id="@+id/action_course_details_to_course_announcements"
app:destination="@id/course_announcements"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
<argument
android:name="courseId"
app:argType="long" />
<argument
android:name="title"
app:argType="string" />
</fragment>
tools:layout="@layout/fragment_schedule"/>
<fragment
android:id="@+id/course_events"
android:name="de.sebse.fuplanner2.ui.details_events.EventsFragment"
@@ -93,10 +54,5 @@
android:id="@+id/nav_notifications"
android:name="de.sebse.fuplanner2.ui.notification.NotificationFragment"
android:label="@string/menu_notifications"
tools:layout="@layout/notification_fragment">
<action
android:id="@+id/action_nav_notifications_to_course_details"
app:destination="@id/course_details"
app:popUpTo="@id/nav_courses" />
</fragment>
tools:layout="@layout/notification_fragment"/>
</navigation>