From cb905fc9a68851ff17b00b18d2c0866e54fae9c0 Mon Sep 17 00:00:00 2001 From: Sebastian Seedorf Date: Fri, 19 Nov 2021 18:20:03 +0100 Subject: [PATCH] Add announcements to compose --- app/build.gradle | 16 +-- .../java/de/sebse/fuplanner2/MainActivity.kt | 4 +- .../fuplanner2/database/AnnouncementDao.kt | 7 +- .../java/de/sebse/fuplanner2/drawerLayout.kt | 6 +- .../fuplanner2/ui/courses/CoursesScreen.kt | 12 +- .../ui/details/CourseDetailsScreen.kt | 30 +++-- .../ui/details/components/AnnouncementItem.kt | 58 +++++++++ .../ui/details/components/LecturerItem.kt | 9 +- .../ui/details/components/QuickLinks.kt | 4 +- .../AnnouncementsViewModel.kt | 20 ++- .../DescriptionFragment.kt | 4 +- .../de/sebse/fuplanner2/ui/theme/Color.kt | 58 +++++++++ .../de/sebse/fuplanner2/ui/theme/Theme.kt | 102 +++++++++++++++ .../java/de/sebse/fuplanner2/ui/theme/Type.kt | 117 ++++++++++++++++++ .../ui/tools/previews/announcement.kt | 40 ++++++ .../previews/course.kt} | 26 ++-- .../tools}/viewmodels/CoursesViewModel.kt | 2 +- .../tools}/viewmodels/DetailsViewModel.kt | 2 +- .../java/de/sebse/fuplanner2/utils/faking.kt | 41 +++++- .../sebse/fuplanner2/worker/CourseWorker.kt | 2 +- .../de/sebse/fuplanner2/worker/SyncWorker.kt | 2 +- .../de/sebse/fuplanner2/ExampleUnitTest.kt | 6 + 22 files changed, 511 insertions(+), 57 deletions(-) create mode 100644 app/src/main/java/de/sebse/fuplanner2/ui/details/components/AnnouncementItem.kt create mode 100644 app/src/main/java/de/sebse/fuplanner2/ui/theme/Color.kt create mode 100644 app/src/main/java/de/sebse/fuplanner2/ui/theme/Theme.kt create mode 100644 app/src/main/java/de/sebse/fuplanner2/ui/theme/Type.kt create mode 100644 app/src/main/java/de/sebse/fuplanner2/ui/tools/previews/announcement.kt rename app/src/main/java/de/sebse/fuplanner2/ui/{details/previewProviders.kt => tools/previews/course.kt} (79%) rename app/src/main/java/de/sebse/fuplanner2/{ => ui/tools}/viewmodels/CoursesViewModel.kt (85%) rename app/src/main/java/de/sebse/fuplanner2/{ => ui/tools}/viewmodels/DetailsViewModel.kt (92%) diff --git a/app/build.gradle b/app/build.gradle index 3c0ff58..c51e213 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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" } diff --git a/app/src/main/java/de/sebse/fuplanner2/MainActivity.kt b/app/src/main/java/de/sebse/fuplanner2/MainActivity.kt index 79dc3bb..83f9e20 100644 --- a/app/src/main/java/de/sebse/fuplanner2/MainActivity.kt +++ b/app/src/main/java/de/sebse/fuplanner2/MainActivity.kt @@ -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() } } 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 fae3638..609b752 100644 --- a/app/src/main/java/de/sebse/fuplanner2/database/AnnouncementDao.kt +++ b/app/src/main/java/de/sebse/fuplanner2/database/AnnouncementDao.kt @@ -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 + @Query("SELECT * FROM announcement WHERE courseId = :courseId ORDER BY createdOn ASC") + fun getAll3(courseId: Long): LiveData> + @Query("SELECT * FROM announcement WHERE uid = :announcementId LIMIT 1") fun getAnnouncementById(announcementId: Long): Announcement @@ -43,4 +46,4 @@ interface AnnouncementDao { @Delete fun delete(announcements: List) -} \ No newline at end of file +} diff --git a/app/src/main/java/de/sebse/fuplanner2/drawerLayout.kt b/app/src/main/java/de/sebse/fuplanner2/drawerLayout.kt index a54bd3c..90c1b91 100644 --- a/app/src/main/java/de/sebse/fuplanner2/drawerLayout.kt +++ b/app/src/main/java/de/sebse/fuplanner2/drawerLayout.kt @@ -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, 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) { } } } 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 80c5f27..7e95128 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 @@ -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) {} } } 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 a0d97bc..3908c3a 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 @@ -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?, 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!!) } } 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 new file mode 100644 index 0000000..cbc0dcb --- /dev/null +++ b/app/src/main/java/de/sebse/fuplanner2/ui/details/components/AnnouncementItem.kt @@ -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) + } +} diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/details/components/LecturerItem.kt b/app/src/main/java/de/sebse/fuplanner2/ui/details/components/LecturerItem.kt index abc979b..0818e07 100644 --- a/app/src/main/java/de/sebse/fuplanner2/ui/details/components/LecturerItem.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/details/components/LecturerItem.kt @@ -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") } } 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 f182693..38a70fd 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 @@ -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) } } diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/details_announcements/AnnouncementsViewModel.kt b/app/src/main/java/de/sebse/fuplanner2/ui/details_announcements/AnnouncementsViewModel.kt index f0058f5..7971057 100644 --- a/app/src/main/java/de/sebse/fuplanner2/ui/details_announcements/AnnouncementsViewModel.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/details_announcements/AnnouncementsViewModel.kt @@ -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 create(modelClass: Class): 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(ctx) { + it.setInputData(workDataOf( + AbstractAccountWorker.KEY_ACCOUNT_NAME to AppAccounts.getInstance().selectedAccount?.name, + AbstractAccountWorker.KEY_COURSE_ID to courseId + )) + } + } + val events: LiveData> = LivePagedListBuilder(factory, 50).build() -} \ No newline at end of file +} 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 index 2213ab7..3b08047 100644 --- 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 @@ -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() { diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/theme/Color.kt b/app/src/main/java/de/sebse/fuplanner2/ui/theme/Color.kt new file mode 100644 index 0000000..1f2ebbd --- /dev/null +++ b/app/src/main/java/de/sebse/fuplanner2/ui/theme/Color.kt @@ -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) diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/theme/Theme.kt b/app/src/main/java/de/sebse/fuplanner2/ui/theme/Theme.kt new file mode 100644 index 0000000..204dffc --- /dev/null +++ b/app/src/main/java/de/sebse/fuplanner2/ui/theme/Theme.kt @@ -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 +) +} diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/theme/Type.kt b/app/src/main/java/de/sebse/fuplanner2/ui/theme/Type.kt new file mode 100644 index 0000000..9fe803d --- /dev/null +++ b/app/src/main/java/de/sebse/fuplanner2/ui/theme/Type.kt @@ -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, + ), +)*/ diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/tools/previews/announcement.kt b/app/src/main/java/de/sebse/fuplanner2/ui/tools/previews/announcement.kt new file mode 100644 index 0000000..7dec87e --- /dev/null +++ b/app/src/main/java/de/sebse/fuplanner2/ui/tools/previews/announcement.kt @@ -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 { + 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 { + override val values = generateSequence { getItem() } + + private fun getItem(): Attachment { + return Attachment( + faker.internet.url(), + faker.strings.title(), + faker.internet.mime() + ) + } +} diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/details/previewProviders.kt b/app/src/main/java/de/sebse/fuplanner2/ui/tools/previews/course.kt similarity index 79% rename from app/src/main/java/de/sebse/fuplanner2/ui/details/previewProviders.kt rename to app/src/main/java/de/sebse/fuplanner2/ui/tools/previews/course.kt index 0d7c0e3..47bf01c 100644 --- a/app/src/main/java/de/sebse/fuplanner2/ui/details/previewProviders.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/tools/previews/course.kt @@ -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 { - 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 { - 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}"}" diff --git a/app/src/main/java/de/sebse/fuplanner2/viewmodels/CoursesViewModel.kt b/app/src/main/java/de/sebse/fuplanner2/ui/tools/viewmodels/CoursesViewModel.kt similarity index 85% rename from app/src/main/java/de/sebse/fuplanner2/viewmodels/CoursesViewModel.kt rename to app/src/main/java/de/sebse/fuplanner2/ui/tools/viewmodels/CoursesViewModel.kt index 6be3638..6072b06 100644 --- a/app/src/main/java/de/sebse/fuplanner2/viewmodels/CoursesViewModel.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/tools/viewmodels/CoursesViewModel.kt @@ -1,4 +1,4 @@ -package de.sebse.fuplanner2.viewmodels +package de.sebse.fuplanner2.ui.tools.viewmodels import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModel diff --git a/app/src/main/java/de/sebse/fuplanner2/viewmodels/DetailsViewModel.kt b/app/src/main/java/de/sebse/fuplanner2/ui/tools/viewmodels/DetailsViewModel.kt similarity index 92% rename from app/src/main/java/de/sebse/fuplanner2/viewmodels/DetailsViewModel.kt rename to app/src/main/java/de/sebse/fuplanner2/ui/tools/viewmodels/DetailsViewModel.kt index ac43155..21dbed1 100644 --- a/app/src/main/java/de/sebse/fuplanner2/viewmodels/DetailsViewModel.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/tools/viewmodels/DetailsViewModel.kt @@ -1,4 +1,4 @@ -package de.sebse.fuplanner2.viewmodels +package de.sebse.fuplanner2.ui.tools.viewmodels import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModel diff --git a/app/src/main/java/de/sebse/fuplanner2/utils/faking.kt b/app/src/main/java/de/sebse/fuplanner2/utils/faking.kt index a296fbd..e7ec917 100644 --- a/app/src/main/java/de/sebse/fuplanner2/utils/faking.kt +++ b/app/src/main/java/de/sebse/fuplanner2/utils/faking.kt @@ -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 "+ diff --git a/app/src/main/java/de/sebse/fuplanner2/worker/CourseWorker.kt b/app/src/main/java/de/sebse/fuplanner2/worker/CourseWorker.kt index 998a5e3..d906d22 100644 --- a/app/src/main/java/de/sebse/fuplanner2/worker/CourseWorker.kt +++ b/app/src/main/java/de/sebse/fuplanner2/worker/CourseWorker.kt @@ -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) diff --git a/app/src/main/java/de/sebse/fuplanner2/worker/SyncWorker.kt b/app/src/main/java/de/sebse/fuplanner2/worker/SyncWorker.kt index bbf336b..e31957c 100644 --- a/app/src/main/java/de/sebse/fuplanner2/worker/SyncWorker.kt +++ b/app/src/main/java/de/sebse/fuplanner2/worker/SyncWorker.kt @@ -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) diff --git a/app/src/test/java/de/sebse/fuplanner2/ExampleUnitTest.kt b/app/src/test/java/de/sebse/fuplanner2/ExampleUnitTest.kt index c52f383..c00414a 100644 --- a/app/src/test/java/de/sebse/fuplanner2/ExampleUnitTest.kt +++ b/app/src/test/java/de/sebse/fuplanner2/ExampleUnitTest.kt @@ -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) + } }