First Compose layout
This commit is contained in:
@@ -32,10 +32,12 @@ android {
|
|||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding true
|
viewBinding true
|
||||||
|
compose true
|
||||||
}
|
}
|
||||||
|
|
||||||
dataBinding {
|
composeOptions {
|
||||||
enabled = true
|
kotlinCompilerVersion kotlin_version
|
||||||
|
kotlinCompilerExtensionVersion compose_version
|
||||||
}
|
}
|
||||||
|
|
||||||
// To inline the bytecode built with JVM target 1.8 into
|
// To inline the bytecode built with JVM target 1.8 into
|
||||||
@@ -54,7 +56,6 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
|
||||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||||
implementation 'androidx.core:core-ktx:1.7.0'
|
implementation 'androidx.core:core-ktx:1.7.0'
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
@@ -77,4 +78,14 @@ dependencies {
|
|||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
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 "com.google.android.material:compose-theme-adapter:$compose_version"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import com.beust.klaxon.KlaxonException
|
|||||||
|
|
||||||
|
|
||||||
data class Lecturer (
|
data class Lecturer (
|
||||||
val fistName: String,
|
val firstName: String,
|
||||||
val lastName: String,
|
val lastName: String,
|
||||||
val email: String,
|
val email: String,
|
||||||
val isResponsible: Boolean
|
val isResponsible: Boolean
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
package de.sebse.fuplanner2.ui.courses
|
|
||||||
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import de.sebse.fuplanner2.R
|
|
||||||
import de.sebse.fuplanner2.database.Course
|
|
||||||
import de.sebse.fuplanner2.ui.CaptionHolder
|
|
||||||
import de.sebse.fuplanner2.ui.CustomHolder
|
|
||||||
import de.sebse.fuplanner2.ui.ListItemHolder
|
|
||||||
import de.sebse.fuplanner2.ui.ViewHolderGenerator
|
|
||||||
import de.sebse.fuplanner2.utils.cast
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class CoursesAdapter(private val onclick: (Course) -> Unit) : RecyclerView.Adapter<CustomHolder>() {
|
|
||||||
|
|
||||||
private val positionalData: ArrayList<Any> = arrayListOf()
|
|
||||||
var dataset: List<Course> = listOf()
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
positionalData.clear()
|
|
||||||
var last: Course? = null
|
|
||||||
dataset.forEach {
|
|
||||||
if (last?.let { last -> it < last } != false)
|
|
||||||
positionalData.add(Pair(it.isSummerSemester, it.year))
|
|
||||||
positionalData.add(it)
|
|
||||||
last = it
|
|
||||||
}
|
|
||||||
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<Pair<Boolean, Int?>>(positionalData[position])?.let { (isSummer, year) ->
|
|
||||||
holder.string.text = when {
|
|
||||||
year == null -> res.getString(R.string.projects)
|
|
||||||
isSummer -> res.getString(R.string.summer_semester, year)
|
|
||||||
else -> res.getString(R.string.winter_semester, year, year+1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is ListItemHolder -> cast<Course>(positionalData[position])?.let {
|
|
||||||
holder.title.text = it.title
|
|
||||||
holder.subLeft.text = it.lecturers.filter { lecturer -> lecturer.isResponsible }.joinToString { lecturer ->
|
|
||||||
res.getString(R.string.full_name, lecturer.fistName.substring(0, 1)+".", lecturer.lastName)
|
|
||||||
}
|
|
||||||
holder.subRight.text = it.type
|
|
||||||
holder.itemView.setOnClickListener { _ -> this.onclick(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int {
|
|
||||||
return when (positionalData[position]) {
|
|
||||||
is Course -> ViewHolderGenerator.HolderType.ITEM.ordinal
|
|
||||||
else -> ViewHolderGenerator.HolderType.HEADER.ordinal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the size of your dataset (invoked by the layout manager)
|
|
||||||
override fun getItemCount() = positionalData.size
|
|
||||||
}
|
|
||||||
@@ -1,25 +1,20 @@
|
|||||||
package de.sebse.fuplanner2.ui.courses
|
package de.sebse.fuplanner2.ui.courses
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.provider.Settings.Global.getString
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
|
import androidx.compose.ui.platform.ComposeView
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import com.google.android.material.composethemeadapter.MdcTheme
|
||||||
import androidx.work.OneTimeWorkRequestBuilder
|
|
||||||
import androidx.work.WorkManager
|
|
||||||
import androidx.work.workDataOf
|
|
||||||
import de.sebse.fuplanner2.R
|
|
||||||
import de.sebse.fuplanner2.auth.AppAccounts
|
|
||||||
import de.sebse.fuplanner2.database.Course
|
|
||||||
import de.sebse.fuplanner2.databinding.ActivityMainBinding
|
|
||||||
import de.sebse.fuplanner2.databinding.FragmentRefreshRecyclerBinding
|
import de.sebse.fuplanner2.databinding.FragmentRefreshRecyclerBinding
|
||||||
import de.sebse.fuplanner2.worker.AbstractAccountWorker.Companion.KEY_ACCOUNT_NAME
|
|
||||||
import de.sebse.fuplanner2.worker.CourseWorker
|
|
||||||
|
|
||||||
class CoursesFragment : Fragment() {
|
class CoursesFragment : Fragment() {
|
||||||
|
|
||||||
@@ -32,42 +27,23 @@ class CoursesFragment : Fragment() {
|
|||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View {
|
||||||
val viewManager = LinearLayoutManager(context)
|
navController = findNavController()
|
||||||
val viewAdapter = CoursesAdapter(this::onClick)
|
|
||||||
coursesViewModel = ViewModelProvider(this).get(CoursesViewModel::class.java)
|
coursesViewModel = ViewModelProvider(this).get(CoursesViewModel::class.java)
|
||||||
binding = FragmentRefreshRecyclerBinding.inflate(inflater, container, false)
|
binding = FragmentRefreshRecyclerBinding.inflate(inflater, container, false)
|
||||||
return inflater.inflate(R.layout.fragment_refresh_recycler, container, false).apply {
|
return ComposeView(requireContext()).apply {
|
||||||
binding.recyclerView.apply {
|
setContent {
|
||||||
//setHasFixedSize(true)
|
MdcTheme {
|
||||||
layoutManager = viewManager
|
val courses = coursesViewModel.text.observeAsState(listOf())
|
||||||
adapter = viewAdapter
|
LazyColumn {
|
||||||
|
items(courses.value) { course -> CourseItem(course) {
|
||||||
|
course.uid?.let {
|
||||||
|
navController.navigate(CoursesFragmentDirections.actionNavHomeToCourseDetails(it, course.title))
|
||||||
|
}
|
||||||
|
} }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
binding.swipeRefreshLayout.setOnRefreshListener {
|
|
||||||
val work = OneTimeWorkRequestBuilder<CourseWorker>()
|
|
||||||
.setInputData(workDataOf(
|
|
||||||
KEY_ACCOUNT_NAME to AppAccounts.getInstance().selectedAccount?.name
|
|
||||||
))
|
|
||||||
.build()
|
|
||||||
WorkManager.getInstance(context.applicationContext)
|
|
||||||
.enqueue(work)
|
|
||||||
WorkManager.getInstance(context.applicationContext)
|
|
||||||
.getWorkInfoByIdLiveData(work.id)
|
|
||||||
.observe(viewLifecycleOwner, {
|
|
||||||
if (it.state.isFinished)
|
|
||||||
binding.swipeRefreshLayout.isRefreshing = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
coursesViewModel.text.observe(viewLifecycleOwner, Observer {
|
|
||||||
viewAdapter.dataset = it
|
|
||||||
})
|
|
||||||
navController = findNavController()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onClick(course: Course) {
|
|
||||||
course.uid?.let {
|
|
||||||
navController.navigate(CoursesFragmentDirections.actionNavHomeToCourseDetails(it, course.title))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
154
app/src/main/java/de/sebse/fuplanner2/ui/courses/components.kt
Normal file
154
app/src/main/java/de/sebse/fuplanner2/ui/courses/components.kt
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
package de.sebse.fuplanner2.ui.courses
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material.*
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Info
|
||||||
|
import androidx.compose.material.icons.outlined.Person
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.dimensionResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import com.google.android.material.composethemeadapter.MdcTheme
|
||||||
|
import de.sebse.fuplanner2.R
|
||||||
|
import de.sebse.fuplanner2.database.Course
|
||||||
|
import de.sebse.fuplanner2.database.Lecturer
|
||||||
|
import de.sebse.fuplanner2.utils.color.getColor
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CourseList(courses: List<Course>, onclick: () -> Unit) {
|
||||||
|
LazyColumn {
|
||||||
|
items(courses) { course -> CourseItem(course, onclick) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CourseItem(course: Course, onclick: () -> Unit) {
|
||||||
|
@Suppress("SimplifiableCallChain")
|
||||||
|
CourseItem(
|
||||||
|
id = course.uid,
|
||||||
|
title = course.title,
|
||||||
|
lecturers = course.lecturers
|
||||||
|
.filter { lecturer -> lecturer.isResponsible }
|
||||||
|
.map { lecturer -> stringResource(R.string.full_name, lecturer.firstName.substring(0, 1)+".", lecturer.lastName) }
|
||||||
|
.joinToString(),
|
||||||
|
type = course.type,
|
||||||
|
onclick = onclick
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CourseItem(id: Long?, title: String, lecturers: String, type: String, onclick: () -> Unit) {
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable(onClick = onclick)
|
||||||
|
.padding(dimensionResource(R.dimen.card_view_margin))
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(dimensionResource(R.dimen.card_view_padding))
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
color = Color(getColor(LocalContext.current, id ?: 0, highContrast = true)),
|
||||||
|
style = MaterialTheme.typography.h6
|
||||||
|
)
|
||||||
|
CourseItemHint(
|
||||||
|
icon = Icons.Outlined.Person,
|
||||||
|
imageAltRes = R.string.lecturers,
|
||||||
|
text = lecturers
|
||||||
|
)
|
||||||
|
CourseItemHint(
|
||||||
|
icon = Icons.Outlined.Info,
|
||||||
|
imageAltRes = R.string.course_type,
|
||||||
|
text = type
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CourseItemHint(icon: ImageVector, @StringRes imageAltRes: Int, text: String) {
|
||||||
|
Row {
|
||||||
|
Icon(
|
||||||
|
icon,
|
||||||
|
contentDescription = stringResource(imageAltRes),
|
||||||
|
modifier = Modifier.padding(
|
||||||
|
start = dimensionResource(R.dimen.card_view_padding)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
style = MaterialTheme.typography.subtitle1,
|
||||||
|
modifier = Modifier.padding(
|
||||||
|
start = dimensionResource(R.dimen.card_view_padding)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
fun CourseListPreview() {
|
||||||
|
val course = Course(
|
||||||
|
userId = 1,
|
||||||
|
lastRefreshed = 1,
|
||||||
|
lvNumber = hashSetOf("1000"),
|
||||||
|
title = "Kurs mit tollem Namen",
|
||||||
|
description = "Beschreibung",
|
||||||
|
internalId = "165464635",
|
||||||
|
isSummerSemester = false,
|
||||||
|
year = 2020,
|
||||||
|
lecturers = listOf(
|
||||||
|
Lecturer(
|
||||||
|
firstName = "Max",
|
||||||
|
lastName = "Maurer",
|
||||||
|
email = "some@mail.com",
|
||||||
|
isResponsible = false
|
||||||
|
),
|
||||||
|
Lecturer(
|
||||||
|
firstName = "Peter",
|
||||||
|
lastName = "Engelbert",
|
||||||
|
email = "coolio@example.com",
|
||||||
|
isResponsible = false
|
||||||
|
)
|
||||||
|
),
|
||||||
|
moduleType = 1236,
|
||||||
|
type = "Type"
|
||||||
|
)
|
||||||
|
MdcTheme {
|
||||||
|
CourseList(listOf(
|
||||||
|
course, course, course
|
||||||
|
)) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun CourseItemPreview() {
|
||||||
|
MdcTheme {
|
||||||
|
CourseItem(
|
||||||
|
12411111111,
|
||||||
|
"Höhere Algorithmik",
|
||||||
|
"M. Berta, P. Parker",
|
||||||
|
"Vorlesung"
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ class DetailsAdapter(private val onQuickLink: (ButtonTypes) -> Unit, private val
|
|||||||
holder.btnResources.setOnClickListener { this.onQuickLink(ButtonTypes.RESOURCES) }
|
holder.btnResources.setOnClickListener { this.onQuickLink(ButtonTypes.RESOURCES) }
|
||||||
}
|
}
|
||||||
is MailHolder -> cast<Lecturer>(positionalData[position])?.let { type ->
|
is MailHolder -> cast<Lecturer>(positionalData[position])?.let { type ->
|
||||||
holder.title.text = res.getString(R.string.full_name, type.fistName, type.lastName)
|
holder.title.text = res.getString(R.string.full_name, type.firstName, type.lastName)
|
||||||
holder.subLeft.text = type.email
|
holder.subLeft.text = type.email
|
||||||
holder.itemView.setOnClickListener { this.onMailTo(type) }
|
holder.itemView.setOnClickListener { this.onMailTo(type) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,8 +69,8 @@ class DetailsFragment : Fragment() {
|
|||||||
intent.type = "text/html"
|
intent.type = "text/html"
|
||||||
intent.data = Uri.fromParts("mailto", lecturer.email, null)
|
intent.data = Uri.fromParts("mailto", lecturer.email, null)
|
||||||
intent.putExtra(Intent.EXTRA_SUBJECT, this.title)
|
intent.putExtra(Intent.EXTRA_SUBJECT, this.title)
|
||||||
intent.putExtra(Intent.EXTRA_TEXT, getString(R.string.email_preview, lecturer.fistName, lecturer.lastName))
|
intent.putExtra(Intent.EXTRA_TEXT, getString(R.string.email_preview, lecturer.firstName, lecturer.lastName))
|
||||||
startActivity(Intent.createChooser(intent, getString(R.string.send_email, lecturer.fistName, lecturer.lastName)))
|
startActivity(Intent.createChooser(intent, getString(R.string.send_email, lecturer.firstName, lecturer.lastName)))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun launchFragment(btnType: DetailsAdapter.ButtonTypes) {
|
private fun launchFragment(btnType: DetailsAdapter.ButtonTypes) {
|
||||||
|
|||||||
@@ -1,24 +1,21 @@
|
|||||||
package de.sebse.fuplanner2.ui.schedule
|
package de.sebse.fuplanner2.ui.schedule
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Configuration
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.SpannableStringBuilder
|
import android.text.SpannableStringBuilder
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import androidx.annotation.ColorInt
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.alamkanak.weekview.WeekView
|
import com.alamkanak.weekview.WeekView
|
||||||
import com.alamkanak.weekview.WeekViewDisplayable
|
|
||||||
import com.alamkanak.weekview.WeekViewEntity
|
import com.alamkanak.weekview.WeekViewEntity
|
||||||
import com.alamkanak.weekview.WeekViewEvent
|
|
||||||
import de.sebse.fuplanner2.R
|
import de.sebse.fuplanner2.R
|
||||||
import de.sebse.fuplanner2.database.AppDatabase
|
import de.sebse.fuplanner2.database.AppDatabase
|
||||||
import de.sebse.fuplanner2.database.Course
|
import de.sebse.fuplanner2.database.Course
|
||||||
import de.sebse.fuplanner2.database.Event
|
import de.sebse.fuplanner2.database.Event
|
||||||
import de.sebse.fuplanner2.ui.schedule.MyCustomPagingAdapter.LoadMoreHandler
|
import de.sebse.fuplanner2.ui.schedule.MyCustomPagingAdapter.LoadMoreHandler
|
||||||
|
import de.sebse.fuplanner2.utils.color.getColor
|
||||||
import de.sebse.fuplanner2.utils.getHtmlSpannedString
|
import de.sebse.fuplanner2.utils.getHtmlSpannedString
|
||||||
import de.sebse.fuplanner2.utils.toTimeString
|
import de.sebse.fuplanner2.utils.toTimeString
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -26,8 +23,6 @@ import kotlinx.coroutines.GlobalScope
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.roundToInt
|
|
||||||
import kotlin.random.Random
|
|
||||||
|
|
||||||
class ScheduleFragment : Fragment() {
|
class ScheduleFragment : Fragment() {
|
||||||
|
|
||||||
@@ -164,56 +159,6 @@ data class ContextEvent(val actCtx: Context, val event: Event) {
|
|||||||
.setStyle(style)
|
.setStyle(style)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ColorInt
|
|
||||||
fun getColor(actCtx: Context, seed: Long): Int {
|
|
||||||
var h = Random(seed).nextInt(0xFFFF)
|
|
||||||
h = h * 360 / 0xffff
|
|
||||||
//int s = 0xff & encodedHash[2];
|
|
||||||
//s = s * 100 / 0xffff;
|
|
||||||
//int v = 0xff & encodedHash[3];
|
|
||||||
//v = v * 100 / 0xffff;
|
|
||||||
|
|
||||||
// range for more beautiful colors
|
|
||||||
h = h / 30 * 30
|
|
||||||
val (s, v) =
|
|
||||||
if (actCtx.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES)
|
|
||||||
Pair(100, 20)//Pair(100, 80)
|
|
||||||
else
|
|
||||||
Pair(60, 100)
|
|
||||||
val (r, g, b) = hsvToRgb(h / 360.0, s / 100.0, v / 100.0)
|
|
||||||
return rgbToColorInt(r, g, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun hsvToRgb(
|
|
||||||
hue: Double,
|
|
||||||
saturation: Double,
|
|
||||||
value: Double
|
|
||||||
): Triple<Double, Double, Double> {
|
|
||||||
val h = (hue * 6).toInt()
|
|
||||||
val f = hue * 6 - h
|
|
||||||
val p = value * (1 - saturation)
|
|
||||||
val q = value * (1 - f * saturation)
|
|
||||||
val t = value * (1 - (1 - f) * saturation)
|
|
||||||
return when (h) {
|
|
||||||
0 -> Triple(value, t, p)
|
|
||||||
1 -> Triple(q, value, p)
|
|
||||||
2 -> Triple(p, value, t)
|
|
||||||
3 -> Triple(p, q, value)
|
|
||||||
4 -> Triple(t, p, value)
|
|
||||||
5 -> Triple(value, p, q)
|
|
||||||
else -> Triple(0.0, 0.0, 0.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ColorInt
|
|
||||||
private fun rgbToColorInt(
|
|
||||||
r: Double,
|
|
||||||
g: Double,
|
|
||||||
b: Double
|
|
||||||
): Int {
|
|
||||||
return 0xFF000000.toInt() + (r*0xFF).roundToInt() * 0x10000 + (g*0xFF).roundToInt() * 0x100 + (b*0xFF).roundToInt()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyCustomPagingAdapter(
|
class MyCustomPagingAdapter(
|
||||||
|
|||||||
@@ -5,11 +5,13 @@ package de.sebse.fuplanner2.utils
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.annotation.TargetApi
|
import android.annotation.TargetApi
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.res.Configuration
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.text.Html
|
import android.text.Html
|
||||||
import android.text.Spanned
|
import android.text.Spanned
|
||||||
import android.text.format.DateFormat
|
import android.text.format.DateFormat
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
import androidx.annotation.PluralsRes
|
import androidx.annotation.PluralsRes
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
@@ -20,6 +22,8 @@ import kotlinx.coroutines.runBlocking
|
|||||||
import java.text.ParseException
|
import java.text.ParseException
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
|
||||||
object console {
|
object console {
|
||||||
@@ -60,6 +64,60 @@ object xml {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object color {
|
||||||
|
@ColorInt
|
||||||
|
fun getColor(actCtx: Context, seed: Long, highContrast: Boolean = false): Int {
|
||||||
|
var h = Random(seed).nextInt(0xFFFF)
|
||||||
|
h = h * 360 / 0xffff
|
||||||
|
//int s = 0xff & encodedHash[2];
|
||||||
|
//s = s * 100 / 0xffff;
|
||||||
|
//int v = 0xff & encodedHash[3];
|
||||||
|
//v = v * 100 / 0xffff;
|
||||||
|
|
||||||
|
// range for more beautiful colors
|
||||||
|
h = h / 30 * 30
|
||||||
|
val isNightMode = actCtx.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
|
||||||
|
val doDarkColors = if (highContrast) !isNightMode else isNightMode
|
||||||
|
val (s, v) =
|
||||||
|
if (doDarkColors)
|
||||||
|
Pair(100, 50)//Pair(100, 80)
|
||||||
|
else
|
||||||
|
Pair(100, 70)
|
||||||
|
val (r, g, b) = hsvToRgb(h / 360.0, s / 100.0, v / 100.0)
|
||||||
|
return rgbToColorInt(r, g, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ColorInt
|
||||||
|
private fun rgbToColorInt(
|
||||||
|
r: Double,
|
||||||
|
g: Double,
|
||||||
|
b: Double
|
||||||
|
): Int {
|
||||||
|
return 0xFF000000.toInt() + (r*0xFF).roundToInt() * 0x10000 + (g*0xFF).roundToInt() * 0x100 + (b*0xFF).roundToInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hsvToRgb(
|
||||||
|
hue: Double,
|
||||||
|
saturation: Double,
|
||||||
|
value: Double
|
||||||
|
): Triple<Double, Double, Double> {
|
||||||
|
val h = (hue * 6).toInt()
|
||||||
|
val f = hue * 6 - h
|
||||||
|
val p = value * (1 - saturation)
|
||||||
|
val q = value * (1 - f * saturation)
|
||||||
|
val t = value * (1 - (1 - f) * saturation)
|
||||||
|
return when (h) {
|
||||||
|
0 -> Triple(value, t, p)
|
||||||
|
1 -> Triple(q, value, p)
|
||||||
|
2 -> Triple(p, value, t)
|
||||||
|
3 -> Triple(p, q, value)
|
||||||
|
4 -> Triple(t, p, value)
|
||||||
|
5 -> Triple(value, p, q)
|
||||||
|
else -> Triple(0.0, 0.0, 0.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline fun <reified T: ListenableWorker> enqueueOneTimeWork(appCtx: Context, workBuilder: (OneTimeWorkRequest.Builder) -> OneTimeWorkRequest.Builder): LiveData<WorkInfo> {
|
inline fun <reified T: ListenableWorker> enqueueOneTimeWork(appCtx: Context, workBuilder: (OneTimeWorkRequest.Builder) -> OneTimeWorkRequest.Builder): LiveData<WorkInfo> {
|
||||||
val work = workBuilder(OneTimeWorkRequestBuilder<T>()).build()
|
val work = workBuilder(OneTimeWorkRequestBuilder<T>()).build()
|
||||||
val workManager = WorkManager.getInstance(appCtx)
|
val workManager = WorkManager.getInstance(appCtx)
|
||||||
|
|||||||
@@ -59,6 +59,7 @@
|
|||||||
<string name="dialog_location_br"><![CDATA[<b>Location:</b><br>%1$s<br>]]></string>
|
<string name="dialog_location_br"><![CDATA[<b>Location:</b><br>%1$s<br>]]></string>
|
||||||
<string name="dialog_time"><![CDATA[<b>Time:</b><br>%1$s - %2$s]]></string>
|
<string name="dialog_time"><![CDATA[<b>Time:</b><br>%1$s - %2$s]]></string>
|
||||||
<string name="dialog_course_br"><![CDATA[<b>Course:</b><br>%1$s<br>]]></string>
|
<string name="dialog_course_br"><![CDATA[<b>Course:</b><br>%1$s<br>]]></string>
|
||||||
|
<string name="course_type">Course Type</string>
|
||||||
<plurals name="not_course_update_text">
|
<plurals name="not_course_update_text">
|
||||||
<item quantity="one">One course message</item>
|
<item quantity="one">One course message</item>
|
||||||
<item quantity="other">%1$d course messages</item>
|
<item quantity="other">%1$d course messages</item>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="FUTheme" parent="Theme.AppCompat.DayNight.DarkActionBar">
|
<style name="FUTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||||
<item name="colorPrimary">@color/colorPrimary</item>
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||||
<item name="colorAccent">@color/colorAccent</item>
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.5.31'
|
ext.kotlin_version = '1.5.31'
|
||||||
|
ext.compose_version = '1.0.5'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|||||||
Reference in New Issue
Block a user