mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-02-23 15:04:45 +01:00
Merge branch 'develop'
This commit is contained in:
commit
c011f550bb
9
.idea/codeStyles/Project.xml
generated
9
.idea/codeStyles/Project.xml
generated
@ -15,6 +15,7 @@
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:android</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
@ -25,6 +26,7 @@
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
@ -36,6 +38,7 @@
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:id</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
@ -46,6 +49,7 @@
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
@ -56,6 +60,7 @@
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
@ -66,6 +71,7 @@
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>style</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
@ -76,6 +82,7 @@
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
@ -87,6 +94,7 @@
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
@ -98,6 +106,7 @@
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
|
1
.idea/dictionaries/Kuba.xml
generated
1
.idea/dictionaries/Kuba.xml
generated
@ -5,6 +5,7 @@
|
||||
<w>ciasteczko</w>
|
||||
<w>csrf</w>
|
||||
<w>edziennik</w>
|
||||
<w>elearning</w>
|
||||
<w>gson</w>
|
||||
<w>hebe</w>
|
||||
<w>idziennik</w>
|
||||
|
@ -152,7 +152,8 @@ dependencies {
|
||||
implementation "pl.droidsonroids.retrofit2:converter-jspoon:1.3.2"
|
||||
|
||||
// Szkolny.eu libraries/forks
|
||||
implementation "eu.szkolny:agendacalendarview:1799f8ef47"
|
||||
implementation "eu.szkolny:android-snowfall:1ca9ea2da3"
|
||||
implementation "eu.szkolny:agendacalendarview:5431f03098"
|
||||
implementation "eu.szkolny:cafebar:5bf0c618de"
|
||||
implementation "eu.szkolny.fslogin:lib:2.0.0"
|
||||
implementation "eu.szkolny:material-about-library:1d5ebaf47c"
|
||||
@ -180,7 +181,6 @@ dependencies {
|
||||
implementation "com.github.bassaer:chatmessageview:2.0.1"
|
||||
implementation "com.github.CanHub:Android-Image-Cropper:2.2.2"
|
||||
implementation "com.github.ChuckerTeam.Chucker:library:3.0.1"
|
||||
implementation "com.github.jetradarmobile:android-snowfall:1.2.0"
|
||||
implementation "com.github.wulkanowy.uonet-request-signer:hebe-jvm:a99ca50a31"
|
||||
implementation("com.heinrichreimersoftware:material-intro") { version { strictly "1.5.8" } }
|
||||
implementation "com.hypertrack:hyperlog:0.0.10"
|
||||
|
3
app/proguard-rules.pro
vendored
3
app/proguard-rules.pro
vendored
@ -32,8 +32,9 @@
|
||||
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.luckynumber.WidgetLuckyNumberProvider
|
||||
|
||||
-keepnames class androidx.appcompat.view.menu.MenuBuilder { setHeaderTitleInt(java.lang.CharSequence); }
|
||||
-keepclassmembernames class androidx.appcompat.view.menu.StandardMenuPopup { private *; }
|
||||
-keepnames class androidx.appcompat.view.menu.MenuPopupHelper { showPopup(int, int, boolean, boolean); }
|
||||
-keepclassmembernames class androidx.appcompat.view.menu.StandardMenuPopup { private *; }
|
||||
-keepclassmembernames class androidx.appcompat.view.menu.MenuItemImpl { private *; }
|
||||
|
||||
-keepclassmembernames class com.mikepenz.materialdrawer.widget.MiniDrawerSliderView { private *; }
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
<h3>Wersja 4.7, 2021-04-07</h3>
|
||||
<h3>Wersja 4.8, 2021-05-26</h3>
|
||||
<ul>
|
||||
<li><u>Szkolny.eu jest teraz open source!</u> Zapraszamy na stronę <a href="https://szkolny.eu/">https://szkolny.eu/</a> po więcej ważnych informacji.</li>
|
||||
<li>Poprawiono wybieranie obrazków (tła nagłówka, tła aplikacji oraz profilu) z dowolnego źródła.</li>
|
||||
<li>Ukończono tłumaczenie na język angielski. @MarcinK50</li>
|
||||
<li>Dodano ekran informacji o kompilacji w Ustawieniach.</li>
|
||||
<li>Zaktualizowano ekran licencji open source.</li>
|
||||
<li>Naprawiono zatrzymanie aplikacji na Androidzie 4.4 i starszych.</li>
|
||||
<li>Naprawiono problemy z połączeniem internetowym na Androidzie 4.4 i starszych.</li>
|
||||
<li>Zoptymalizowano wielkość aplikacji.</li>
|
||||
<li>Dodano ikony dla powiadomień. @Luncenok</li>
|
||||
<li>Terminarz: opcje konfiguracji, widok kompaktowy, grupowanie wydarzeń, znaczki nieprzeczytanych, nowe ikony i wiele innych usprawnień.</li>
|
||||
<li>Wiadomości: usprawiono wyszukiwanie - zapisywanie szukanego tekstu po wejściu w wiadomość.</li>
|
||||
<li>Wiadomości: dodano opcję konfiguracji podpisu przy wysyłaniu wiadomości.</li>
|
||||
<li>Plan lekcji: dodano znacznik aktualnej pory dnia w planie lekcji.</li>
|
||||
<li>Powiadomienia: dodano szczegółowy opis po rozwinięciu.</li>
|
||||
<li>Wydarzenia: nowy rodzaj "lekcja online".</li>
|
||||
<li>Naprawiono odbieranie nagrody w easter egg'u.</li>
|
||||
</ul>
|
||||
<br>
|
||||
<br>
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
/*secret password - removed for source code publication*/
|
||||
static toys AES_IV[16] = {
|
||||
0xda, 0x9f, 0xd4, 0x2b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
0x71, 0xcf, 0xdf, 0x13, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
|
||||
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);
|
||||
|
||||
|
@ -15,6 +15,7 @@ import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.*
|
||||
import android.text.style.CharacterStyle
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.StrikethroughSpan
|
||||
import android.text.style.StyleSpan
|
||||
@ -552,28 +553,46 @@ fun CharSequence?.asBoldSpannable(): Spannable {
|
||||
spannable.setSpan(StyleSpan(Typeface.BOLD), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
return spannable
|
||||
}
|
||||
fun CharSequence.asSpannable(vararg spans: Any, substring: String? = null, ignoreCase: Boolean = false, ignoreDiacritics: Boolean = false): Spannable {
|
||||
fun CharSequence.asSpannable(
|
||||
vararg spans: CharacterStyle,
|
||||
substring: CharSequence? = null,
|
||||
ignoreCase: Boolean = false,
|
||||
ignoreDiacritics: Boolean = false
|
||||
): Spannable {
|
||||
val spannable = SpannableString(this)
|
||||
if (substring == null) {
|
||||
spans.forEach {
|
||||
spannable.setSpan(it, 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
}
|
||||
else if (substring.isNotEmpty()) {
|
||||
val string =
|
||||
if (ignoreDiacritics)
|
||||
substring?.let { substr ->
|
||||
val string = if (ignoreDiacritics)
|
||||
this.cleanDiacritics()
|
||||
else this
|
||||
else
|
||||
this
|
||||
val search = if (ignoreDiacritics)
|
||||
substr.cleanDiacritics()
|
||||
else
|
||||
substr.toString()
|
||||
|
||||
var index = string.indexOf(substring, ignoreCase = ignoreCase)
|
||||
.takeIf { it != -1 } ?: indexOf(substring, ignoreCase = ignoreCase)
|
||||
while (index >= 0) {
|
||||
var index = 0
|
||||
do {
|
||||
index = string.indexOf(
|
||||
string = search,
|
||||
startIndex = index,
|
||||
ignoreCase = ignoreCase
|
||||
)
|
||||
|
||||
if (index >= 0) {
|
||||
spans.forEach {
|
||||
spannable.setSpan(it, index, index + substring.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
spannable.setSpan(
|
||||
CharacterStyle.wrap(it),
|
||||
index,
|
||||
index + substring.length,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
index = string.indexOf(substring, startIndex = index + 1, ignoreCase = ignoreCase)
|
||||
.takeIf { it != -1 } ?: indexOf(substring, startIndex = index + 1, ignoreCase = ignoreCase)
|
||||
index += substring.length.coerceAtLeast(1)
|
||||
}
|
||||
} while (index >= 0)
|
||||
|
||||
} ?: spans.forEach {
|
||||
spannable.setSpan(it, 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
return spannable
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import androidx.lifecycle.Observer
|
||||
import androidx.navigation.NavOptions
|
||||
import com.danimahardhika.cafebar.CafeBar
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.jetradarmobile.snowfall.SnowfallView
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
@ -81,12 +82,9 @@ import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.webpush.WebPushFragment
|
||||
import pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoTouch
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import pl.szczodrzynski.edziennik.utils.*
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.dpToPx
|
||||
import pl.szczodrzynski.edziennik.utils.appManagerIntentList
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.NavTarget
|
||||
import pl.szczodrzynski.navlib.*
|
||||
@ -470,9 +468,21 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
||||
|
||||
// IT'S WINTER MY DUDES
|
||||
val today = Date.getToday()
|
||||
if ((today.month == 12 || today.month == 1) && app.config.ui.snowfall) {
|
||||
if ((today.month % 11 == 1) && app.config.ui.snowfall) {
|
||||
b.rootFrame.addView(layoutInflater.inflate(R.layout.snowfall, b.rootFrame, false))
|
||||
}
|
||||
else if (app.config.ui.eggfall && BigNightUtil().isDataWielkanocyNearDzisiaj()) {
|
||||
val eggfall = layoutInflater.inflate(R.layout.eggfall, b.rootFrame, false) as SnowfallView
|
||||
eggfall.setSnowflakeBitmaps(listOf(
|
||||
BitmapFactory.decodeResource(resources, R.drawable.egg1),
|
||||
BitmapFactory.decodeResource(resources, R.drawable.egg2),
|
||||
BitmapFactory.decodeResource(resources, R.drawable.egg3),
|
||||
BitmapFactory.decodeResource(resources, R.drawable.egg4),
|
||||
BitmapFactory.decodeResource(resources, R.drawable.egg5),
|
||||
BitmapFactory.decodeResource(resources, R.drawable.egg6)
|
||||
))
|
||||
b.rootFrame.addView(eggfall)
|
||||
}
|
||||
|
||||
// WHAT'S NEW DIALOG
|
||||
if (app.config.appVersion < BuildConfig.VERSION_CODE) {
|
||||
|
@ -6,6 +6,7 @@ package pl.szczodrzynski.edziennik.config
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import pl.szczodrzynski.edziennik.BuildConfig
|
||||
import pl.szczodrzynski.edziennik.config.utils.get
|
||||
import pl.szczodrzynski.edziennik.config.utils.getIntList
|
||||
import pl.szczodrzynski.edziennik.config.utils.set
|
||||
@ -123,6 +124,19 @@ class ConfigSync(private val config: Config) {
|
||||
|
||||
private var mRegisterAvailability: Map<String, RegisterAvailabilityStatus>? = null
|
||||
var registerAvailability: Map<String, RegisterAvailabilityStatus>
|
||||
get() { mRegisterAvailability = mRegisterAvailability ?: config.values.get("registerAvailability", null as String?)?.let { it -> gson.fromJson<Map<String, RegisterAvailabilityStatus>>(it, object: TypeToken<Map<String, RegisterAvailabilityStatus>>(){}.type) }; return mRegisterAvailability ?: mapOf() }
|
||||
set(value) { config.setMap("registerAvailability", value); mRegisterAvailability = value }
|
||||
get() {
|
||||
val flavor = config.values.get("registerAvailabilityFlavor", null as String?)
|
||||
if (BuildConfig.FLAVOR != flavor)
|
||||
return mapOf()
|
||||
|
||||
mRegisterAvailability = mRegisterAvailability ?: config.values.get("registerAvailability", null as String?)?.let { it ->
|
||||
gson.fromJson(it, object: TypeToken<Map<String, RegisterAvailabilityStatus>>(){}.type)
|
||||
}
|
||||
return mRegisterAvailability ?: mapOf()
|
||||
}
|
||||
set(value) {
|
||||
config.setMap("registerAvailability", value)
|
||||
config.set("registerAvailabilityFlavor", BuildConfig.FLAVOR)
|
||||
mRegisterAvailability = value
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,11 @@ class ConfigUI(private val config: Config) {
|
||||
get() { mSnowfall = mSnowfall ?: config.values.get("snowfall", false); return mSnowfall ?: false }
|
||||
set(value) { config.set("snowfall", value); mSnowfall = value }
|
||||
|
||||
private var mEggfall: Boolean? = null
|
||||
var eggfall: Boolean
|
||||
get() { mEggfall = mEggfall ?: config.values.get("eggfall", false); return mEggfall ?: false }
|
||||
set(value) { config.set("eggfall", value); mEggfall = value }
|
||||
|
||||
private var mBottomSheetOpened: Boolean? = null
|
||||
var bottomSheetOpened: Boolean
|
||||
get() { mBottomSheetOpened = mBottomSheetOpened ?: config.values.get("bottomSheetOpened", false); return mBottomSheetOpened ?: false }
|
||||
|
@ -15,8 +15,58 @@ class ProfileConfigUI(private val config: ProfileConfig) {
|
||||
get() { mAgendaViewType = mAgendaViewType ?: config.values.get("agendaViewType", 0); return mAgendaViewType ?: AGENDA_DEFAULT }
|
||||
set(value) { config.set("agendaViewType", value); mAgendaViewType = value }
|
||||
|
||||
private var mAgendaCompactMode: Boolean? = null
|
||||
var agendaCompactMode: Boolean
|
||||
get() { mAgendaCompactMode = mAgendaCompactMode ?: config.values.get("agendaCompactMode", false); return mAgendaCompactMode ?: false }
|
||||
set(value) { config.set("agendaCompactMode", value); mAgendaCompactMode = value }
|
||||
|
||||
private var mAgendaGroupByType: Boolean? = null
|
||||
var agendaGroupByType: Boolean
|
||||
get() { mAgendaGroupByType = mAgendaGroupByType ?: config.values.get("agendaGroupByType", false); return mAgendaGroupByType ?: false }
|
||||
set(value) { config.set("agendaGroupByType", value); mAgendaGroupByType = value }
|
||||
|
||||
private var mAgendaLessonChanges: Boolean? = null
|
||||
var agendaLessonChanges: Boolean
|
||||
get() { mAgendaLessonChanges = mAgendaLessonChanges ?: config.values.get("agendaLessonChanges", true); return mAgendaLessonChanges ?: true }
|
||||
set(value) { config.set("agendaLessonChanges", value); mAgendaLessonChanges = value }
|
||||
|
||||
private var mAgendaTeacherAbsence: Boolean? = null
|
||||
var agendaTeacherAbsence: Boolean
|
||||
get() { mAgendaTeacherAbsence = mAgendaTeacherAbsence ?: config.values.get("agendaTeacherAbsence", true); return mAgendaTeacherAbsence ?: true }
|
||||
set(value) { config.set("agendaTeacherAbsence", value); mAgendaTeacherAbsence = value }
|
||||
|
||||
private var mAgendaElearningMark: Boolean? = null
|
||||
var agendaElearningMark: Boolean
|
||||
get() { mAgendaElearningMark = mAgendaElearningMark ?: config.values.get("agendaElearningMark", false); return mAgendaElearningMark ?: false }
|
||||
set(value) { config.set("agendaElearningMark", value); mAgendaElearningMark = value }
|
||||
|
||||
private var mAgendaElearningGroup: Boolean? = null
|
||||
var agendaElearningGroup: Boolean
|
||||
get() { mAgendaElearningGroup = mAgendaElearningGroup ?: config.values.get("agendaElearningGroup", true); return mAgendaElearningGroup ?: true }
|
||||
set(value) { config.set("agendaElearningGroup", value); mAgendaElearningGroup = value }
|
||||
|
||||
private var mHomeCards: List<HomeCardModel>? = null
|
||||
var homeCards: List<HomeCardModel>
|
||||
get() { mHomeCards = mHomeCards ?: config.values.get("homeCards", listOf(), HomeCardModel::class.java); return mHomeCards ?: listOf() }
|
||||
set(value) { config.set("homeCards", value); mHomeCards = value }
|
||||
|
||||
private var mMessagesGreetingOnCompose: Boolean? = null
|
||||
var messagesGreetingOnCompose: Boolean
|
||||
get() { mMessagesGreetingOnCompose = mMessagesGreetingOnCompose ?: config.values.get("messagesGreetingOnCompose", true); return mMessagesGreetingOnCompose ?: true }
|
||||
set(value) { config.set("messagesGreetingOnCompose", value); mMessagesGreetingOnCompose = value }
|
||||
|
||||
private var mMessagesGreetingOnReply: Boolean? = null
|
||||
var messagesGreetingOnReply: Boolean
|
||||
get() { mMessagesGreetingOnReply = mMessagesGreetingOnReply ?: config.values.get("messagesGreetingOnReply", true); return mMessagesGreetingOnReply ?: true }
|
||||
set(value) { config.set("messagesGreetingOnReply", value); mMessagesGreetingOnReply = value }
|
||||
|
||||
private var mMessagesGreetingOnForward: Boolean? = null
|
||||
var messagesGreetingOnForward: Boolean
|
||||
get() { mMessagesGreetingOnForward = mMessagesGreetingOnForward ?: config.values.get("messagesGreetingOnForward", false); return mMessagesGreetingOnForward ?: false }
|
||||
set(value) { config.set("messagesGreetingOnForward", value); mMessagesGreetingOnForward = value }
|
||||
|
||||
private var mMessagesGreetingText: String? = null
|
||||
var messagesGreetingText: String?
|
||||
get() { mMessagesGreetingText = mMessagesGreetingText ?: config.values["messagesGreetingText"]; return mMessagesGreetingText }
|
||||
set(value) { config.set("messagesGreetingText", value); mMessagesGreetingText = value }
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.fixName
|
||||
import pl.szczodrzynski.edziennik.singleOrNull
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import java.net.URLEncoder
|
||||
|
||||
class MobidziennikWebMessagesAll(override val data: DataMobidziennik,
|
||||
override val lastSync: Long?,
|
||||
@ -27,7 +28,8 @@ class MobidziennikWebMessagesAll(override val data: DataMobidziennik,
|
||||
}
|
||||
|
||||
init {
|
||||
webGet(TAG, "/dziennik/wyszukiwarkawiadomosci?q=+") { text ->
|
||||
val query = URLEncoder.encode(data.profile?.studentNameLong ?: "a", "UTF-8")
|
||||
webGet(TAG, "/dziennik/wyszukiwarkawiadomosci?q=$query") { text ->
|
||||
MobidziennikLuckyNumberExtractor(data, text)
|
||||
|
||||
val doc = Jsoup.parse(text)
|
||||
|
@ -46,6 +46,6 @@ object Signing {
|
||||
|
||||
/*fun provideKey(param1: String, param2: Long): ByteArray {*/
|
||||
fun pleaseStopRightNow(param1: String, param2: Long): ByteArray {
|
||||
return "$param1.MTIzNDU2Nzg5MDLPrcQX7M===.$param2".sha256()
|
||||
return "$param1.MTIzNDU2Nzg5MDZ/2nExVD===.$param2".sha256()
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.*
|
||||
import pl.szczodrzynski.edziennik.getNotificationTitle
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||
|
||||
class Notifications(val app: App, val notifications: MutableList<Notification>, val profiles: List<Profile>) {
|
||||
companion object {
|
||||
@ -42,13 +43,22 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
|
||||
val text = app.getString(
|
||||
R.string.notification_lesson_change_format,
|
||||
lesson.getDisplayChangeType(app),
|
||||
if (lesson.displayDate == null) "" else lesson.displayDate!!.formattedString,
|
||||
lesson.displayDate?.formattedString ?: "",
|
||||
lesson.changeSubjectName
|
||||
)
|
||||
val textLong = app.getString(
|
||||
R.string.notification_lesson_change_long_format,
|
||||
lesson.getDisplayChangeType(app),
|
||||
lesson.displayDate?.formattedString ?: "-",
|
||||
lesson.displayDate?.weekDay?.let { Week.getFullDayName(it) } ?: "-",
|
||||
lesson.changeSubjectName,
|
||||
lesson.changeTeacherName
|
||||
)
|
||||
notifications += Notification(
|
||||
id = Notification.buildId(lesson.profileId, Notification.TYPE_TIMETABLE_LESSON_CHANGE, lesson.id),
|
||||
title = app.getNotificationTitle(Notification.TYPE_TIMETABLE_LESSON_CHANGE),
|
||||
text = text,
|
||||
textLong = textLong,
|
||||
type = Notification.TYPE_TIMETABLE_LESSON_CHANGE,
|
||||
profileId = lesson.profileId,
|
||||
profileName = profiles.singleOrNull { it.id == lesson.profileId }?.name,
|
||||
@ -79,11 +89,21 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
|
||||
event.date.formattedString,
|
||||
event.subjectLongName
|
||||
)
|
||||
val textLong = app.getString(
|
||||
R.string.notification_event_long_format,
|
||||
event.typeName ?: "-",
|
||||
event.subjectLongName ?: "-",
|
||||
event.date.formattedString,
|
||||
Week.getFullDayName(event.date.weekDay),
|
||||
event.time?.stringHM ?: app.getString(R.string.event_all_day),
|
||||
event.topic.take(200)
|
||||
)
|
||||
val type = if (event.type == Event.TYPE_HOMEWORK) Notification.TYPE_NEW_HOMEWORK else Notification.TYPE_NEW_EVENT
|
||||
notifications += Notification(
|
||||
id = Notification.buildId(event.profileId, type, event.id),
|
||||
title = app.getNotificationTitle(type),
|
||||
text = text,
|
||||
textLong = textLong,
|
||||
type = type,
|
||||
profileId = event.profileId,
|
||||
profileName = profiles.singleOrNull { it.id == event.profileId }?.name,
|
||||
@ -102,11 +122,22 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
|
||||
event.date.formattedString,
|
||||
event.topic
|
||||
)
|
||||
val textLong = app.getString(
|
||||
R.string.notification_shared_event_long_format,
|
||||
event.sharedByName,
|
||||
event.typeName ?: "-",
|
||||
event.subjectLongName ?: "-",
|
||||
event.date.formattedString,
|
||||
Week.getFullDayName(event.date.weekDay),
|
||||
event.time?.stringHM ?: app.getString(R.string.event_all_day),
|
||||
event.topic.take(200)
|
||||
)
|
||||
val type = if (event.type == Event.TYPE_HOMEWORK) Notification.TYPE_NEW_HOMEWORK else Notification.TYPE_NEW_EVENT
|
||||
notifications += Notification(
|
||||
id = Notification.buildId(event.profileId, type, event.id),
|
||||
title = app.getNotificationTitle(type),
|
||||
text = text,
|
||||
textLong = textLong,
|
||||
type = type,
|
||||
profileId = event.profileId,
|
||||
profileName = profiles.singleOrNull { it.id == event.profileId }?.name,
|
||||
@ -130,10 +161,20 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
|
||||
gradeName,
|
||||
grade.subjectLongName
|
||||
)
|
||||
val textLong = app.getString(
|
||||
R.string.notification_grade_long_format,
|
||||
gradeName,
|
||||
grade.weight.toString(),
|
||||
grade.subjectLongName ?: "-",
|
||||
grade.category ?: "-",
|
||||
grade.description ?: "-",
|
||||
grade.teacherName ?: "-"
|
||||
)
|
||||
notifications += Notification(
|
||||
id = Notification.buildId(grade.profileId, Notification.TYPE_NEW_GRADE, grade.id),
|
||||
title = app.getNotificationTitle(Notification.TYPE_NEW_GRADE),
|
||||
text = text,
|
||||
textLong = textLong,
|
||||
type = Notification.TYPE_NEW_GRADE,
|
||||
profileId = grade.profileId,
|
||||
profileName = profiles.singleOrNull { it.id == grade.profileId }?.name,
|
||||
@ -158,10 +199,17 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
|
||||
notice.teacherName,
|
||||
Date.fromMillis(notice.addedDate).formattedString
|
||||
)
|
||||
val textLong = app.getString(
|
||||
R.string.notification_notice_long_format,
|
||||
noticeTypeStr,
|
||||
notice.teacherName ?: "-",
|
||||
notice.text.take(200)
|
||||
)
|
||||
notifications += Notification(
|
||||
id = Notification.buildId(notice.profileId, Notification.TYPE_NEW_NOTICE, notice.id),
|
||||
title = app.getNotificationTitle(Notification.TYPE_NEW_NOTICE),
|
||||
text = text,
|
||||
textLong = textLong,
|
||||
type = Notification.TYPE_NEW_NOTICE,
|
||||
profileId = notice.profileId,
|
||||
profileName = profiles.singleOrNull { it.id == notice.profileId }?.name,
|
||||
@ -193,10 +241,21 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
|
||||
attendance.subjectLongName,
|
||||
attendance.date.formattedString
|
||||
)
|
||||
val textLong = app.getString(
|
||||
R.string.notification_attendance_long_format,
|
||||
attendanceTypeStr,
|
||||
attendance.date.formattedString,
|
||||
attendance.startTime?.stringHM ?: "-",
|
||||
attendance.lessonNumber ?: "-",
|
||||
attendance.subjectLongName ?: "-",
|
||||
attendance.teacherName ?: "-",
|
||||
attendance.lessonTopic ?: "-"
|
||||
)
|
||||
notifications += Notification(
|
||||
id = Notification.buildId(attendance.profileId, Notification.TYPE_NEW_ATTENDANCE, attendance.id),
|
||||
title = app.getNotificationTitle(Notification.TYPE_NEW_ATTENDANCE),
|
||||
text = text,
|
||||
textLong = textLong,
|
||||
type = Notification.TYPE_NEW_ATTENDANCE,
|
||||
profileId = attendance.profileId,
|
||||
profileName = profiles.singleOrNull { it.id == attendance.profileId }?.name,
|
||||
|
@ -8,6 +8,9 @@ import android.util.SparseIntArray
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.util.forEach
|
||||
import androidx.core.util.set
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import com.mikepenz.iconics.utils.*
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_SERVER_MESSAGE
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
@ -107,6 +110,10 @@ class PostNotifications(val app: App, nList: List<AppNotification>) {
|
||||
.setContentText(buildSummaryText(summaryCounts))
|
||||
.setTicker(newNotificationsText)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setLargeIcon(IconicsDrawable(app).apply {
|
||||
icon = CommunityMaterial.Icon.cmd_bell_ring_outline
|
||||
colorRes = R.color.colorPrimary
|
||||
}.toBitmap())
|
||||
.setStyle(NotificationCompat.InboxStyle()
|
||||
.also {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
@ -137,8 +144,11 @@ class PostNotifications(val app: App, nList: List<AppNotification>) {
|
||||
.setSubText(if (it.type == TYPE_SERVER_MESSAGE) null else it.title)
|
||||
.setTicker("${it.profileName}: ${it.title}")
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setLargeIcon(IconicsDrawable(app, it.getLargeIcon()).apply {
|
||||
colorRes = R.color.colorPrimary
|
||||
}.toBitmap())
|
||||
.setStyle(NotificationCompat.BigTextStyle()
|
||||
.bigText(it.text))
|
||||
.bigText(it.textLong ?: it.text))
|
||||
.setWhen(it.addedDate)
|
||||
.addDefaults()
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||
@ -160,6 +170,10 @@ class PostNotifications(val app: App, nList: List<AppNotification>) {
|
||||
.setContentText(buildSummaryText(summaryCounts))
|
||||
.setTicker(newNotificationsText)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setLargeIcon(IconicsDrawable(app).apply {
|
||||
icon = CommunityMaterial.Icon.cmd_bell_ring_outline
|
||||
colorRes = R.color.colorPrimary
|
||||
}.toBitmap())
|
||||
.addDefaults()
|
||||
.setGroupSummary(true)
|
||||
.setContentIntent(summaryIntent)
|
||||
|
@ -43,7 +43,7 @@ import pl.szczodrzynski.edziennik.data.db.migration.*
|
||||
LibrusLesson::class,
|
||||
TimetableManual::class,
|
||||
Metadata::class
|
||||
], version = 91)
|
||||
], version = 93)
|
||||
@TypeConverters(
|
||||
ConverterTime::class,
|
||||
ConverterDate::class,
|
||||
@ -176,7 +176,9 @@ abstract class AppDb : RoomDatabase() {
|
||||
Migration88(),
|
||||
Migration89(),
|
||||
Migration90(),
|
||||
Migration91()
|
||||
Migration91(),
|
||||
Migration92(),
|
||||
Migration93()
|
||||
).allowMainThreadQueries().build()
|
||||
}
|
||||
}
|
||||
|
@ -9,30 +9,9 @@ import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_CLASS_EVENT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_DEFAULT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_ESSAY
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_EXAM
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_EXCURSION
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_INFORMATION
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_PROJECT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_PT_MEETING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_READING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_SHORT_QUIZ
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_CLASS_EVENT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_DEFAULT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_ESSAY
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_EXAM
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_EXCURSION
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_INFORMATION
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_PROJECT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_PT_MEETING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_READING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_SHORT_QUIZ
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.EventType
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.EventType.Companion.SOURCE_DEFAULT
|
||||
|
||||
@Dao
|
||||
abstract class EventTypeDao {
|
||||
@ -58,19 +37,18 @@ abstract class EventTypeDao {
|
||||
abstract val allNow: List<EventType>
|
||||
|
||||
fun addDefaultTypes(context: Context, profileId: Int): List<EventType> {
|
||||
val typeList = listOf(
|
||||
EventType(profileId, TYPE_HOMEWORK, context.getString(R.string.event_type_homework), COLOR_HOMEWORK),
|
||||
EventType(profileId, TYPE_DEFAULT, context.getString(R.string.event_other), COLOR_DEFAULT),
|
||||
EventType(profileId, TYPE_EXAM, context.getString(R.string.event_exam), COLOR_EXAM),
|
||||
EventType(profileId, TYPE_SHORT_QUIZ, context.getString(R.string.event_short_quiz), COLOR_SHORT_QUIZ),
|
||||
EventType(profileId, TYPE_ESSAY, context.getString(R.string.event_essay), COLOR_ESSAY),
|
||||
EventType(profileId, TYPE_PROJECT, context.getString(R.string.event_project), COLOR_PROJECT),
|
||||
EventType(profileId, TYPE_PT_MEETING, context.getString(R.string.event_pt_meeting), COLOR_PT_MEETING),
|
||||
EventType(profileId, TYPE_EXCURSION, context.getString(R.string.event_excursion), COLOR_EXCURSION),
|
||||
EventType(profileId, TYPE_READING, context.getString(R.string.event_reading), COLOR_READING),
|
||||
EventType(profileId, TYPE_CLASS_EVENT, context.getString(R.string.event_class_event), COLOR_CLASS_EVENT),
|
||||
EventType(profileId, TYPE_INFORMATION, context.getString(R.string.event_information), COLOR_INFORMATION)
|
||||
var order = 100
|
||||
val colorMap = EventType.getTypeColorMap()
|
||||
val typeList = EventType.getTypeNameMap().map { (id, name) ->
|
||||
EventType(
|
||||
profileId = profileId,
|
||||
id = id,
|
||||
name = context.getString(name),
|
||||
color = colorMap[id] ?: COLOR_DEFAULT,
|
||||
order = order++,
|
||||
source = SOURCE_DEFAULT
|
||||
)
|
||||
}
|
||||
addAll(typeList)
|
||||
return typeList
|
||||
}
|
||||
|
@ -84,6 +84,8 @@ abstract class TimetableDao : BaseDao<Lesson, LessonFull> {
|
||||
"LIMIT 1")
|
||||
fun getBetweenDates(dateFrom: Date, dateTo: Date) =
|
||||
getRaw("$QUERY WHERE (type != 3 AND date >= '${dateFrom.stringY_m_d}' AND date <= '${dateTo.stringY_m_d}') OR ((type = 3 OR type = 1) AND oldDate >= '${dateFrom.stringY_m_d}' AND oldDate <= '${dateTo.stringY_m_d}') $ORDER_BY")
|
||||
fun getChanges(profileId: Int) =
|
||||
getRaw("$QUERY WHERE timetable.profileId = $profileId AND $IS_CHANGED $ORDER_BY")
|
||||
|
||||
// GET ALL - NOW
|
||||
fun getAllNow(profileId: Int) =
|
||||
|
@ -45,6 +45,7 @@ open class Event(
|
||||
var addedDate: Long = System.currentTimeMillis()
|
||||
) : Keepable() {
|
||||
companion object {
|
||||
const val TYPE_ELEARNING = -5L
|
||||
const val TYPE_UNDEFINED = -2L
|
||||
const val TYPE_HOMEWORK = -1L
|
||||
const val TYPE_DEFAULT = 0L
|
||||
@ -57,7 +58,7 @@ open class Event(
|
||||
const val TYPE_READING = 7L
|
||||
const val TYPE_CLASS_EVENT = 8L
|
||||
const val TYPE_INFORMATION = 9L
|
||||
const val TYPE_TEACHER_ABSENCE = 10L
|
||||
const val COLOR_ELEARNING = 0xfff57f17.toInt()
|
||||
const val COLOR_HOMEWORK = 0xff795548.toInt()
|
||||
const val COLOR_DEFAULT = 0xffffc107.toInt()
|
||||
const val COLOR_EXAM = 0xfff44336.toInt()
|
||||
@ -69,7 +70,6 @@ open class Event(
|
||||
const val COLOR_READING = 0xFFFFEB3B.toInt()
|
||||
const val COLOR_CLASS_EVENT = 0xff388e3c.toInt()
|
||||
const val COLOR_INFORMATION = 0xff039be5.toInt()
|
||||
const val COLOR_TEACHER_ABSENCE = 0xff039be5.toInt()
|
||||
}
|
||||
|
||||
@ColumnInfo(name = "eventAddedManually")
|
||||
@ -116,14 +116,7 @@ open class Event(
|
||||
var showAsUnseen: Boolean? = null
|
||||
|
||||
val startTimeCalendar: Calendar
|
||||
get() = Calendar.getInstance().also { it.set(
|
||||
date.year,
|
||||
date.month - 1,
|
||||
date.day,
|
||||
time?.hour ?: 0,
|
||||
time?.minute ?: 0,
|
||||
time?.second ?: 0
|
||||
) }
|
||||
get() = date.getAsCalendar(time)
|
||||
|
||||
val endTimeCalendar: Calendar
|
||||
get() = startTimeCalendar.also {
|
||||
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.entity;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
|
||||
@Entity(tableName = "eventTypes",
|
||||
primaryKeys = {"profileId", "eventType"})
|
||||
public class EventType {
|
||||
public int profileId;
|
||||
|
||||
@ColumnInfo(name = "eventType")
|
||||
public long id;
|
||||
|
||||
@ColumnInfo(name = "eventTypeName")
|
||||
public String name;
|
||||
@ColumnInfo(name = "eventTypeColor")
|
||||
public int color;
|
||||
|
||||
public EventType(int profileId, long id, String name, int color) {
|
||||
this.profileId = profileId;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public EventType(int profileId, int id, String name, String color) {
|
||||
this(profileId, id, name, Color.parseColor(color));
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-4-19.
|
||||
*/
|
||||
package pl.szczodrzynski.edziennik.data.db.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_CLASS_EVENT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_DEFAULT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_ELEARNING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_ESSAY
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_EXAM
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_EXCURSION
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_INFORMATION
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_PROJECT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_PT_MEETING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_READING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_SHORT_QUIZ
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_CLASS_EVENT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_DEFAULT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_ELEARNING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_ESSAY
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_EXAM
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_EXCURSION
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_INFORMATION
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_PROJECT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_PT_MEETING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_READING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_SHORT_QUIZ
|
||||
|
||||
@Entity(
|
||||
tableName = "eventTypes",
|
||||
primaryKeys = ["profileId", "eventType"]
|
||||
)
|
||||
class EventType(
|
||||
val profileId: Int,
|
||||
|
||||
@ColumnInfo(name = "eventType")
|
||||
val id: Long,
|
||||
|
||||
@ColumnInfo(name = "eventTypeName")
|
||||
val name: String,
|
||||
@ColumnInfo(name = "eventTypeColor")
|
||||
val color: Int,
|
||||
@ColumnInfo(name = "eventTypeOrder")
|
||||
var order: Int = id.toInt(),
|
||||
@ColumnInfo(name = "eventTypeSource")
|
||||
val source: Int = SOURCE_REGISTER
|
||||
) {
|
||||
companion object {
|
||||
const val SOURCE_DEFAULT = 0
|
||||
const val SOURCE_REGISTER = 1
|
||||
const val SOURCE_CUSTOM = 2
|
||||
const val SOURCE_SHARED = 3
|
||||
|
||||
fun getTypeColorMap() = mapOf(
|
||||
TYPE_ELEARNING to COLOR_ELEARNING,
|
||||
TYPE_HOMEWORK to COLOR_HOMEWORK,
|
||||
TYPE_DEFAULT to COLOR_DEFAULT,
|
||||
TYPE_EXAM to COLOR_EXAM,
|
||||
TYPE_SHORT_QUIZ to COLOR_SHORT_QUIZ,
|
||||
TYPE_ESSAY to COLOR_ESSAY,
|
||||
TYPE_PROJECT to COLOR_PROJECT,
|
||||
TYPE_PT_MEETING to COLOR_PT_MEETING,
|
||||
TYPE_EXCURSION to COLOR_EXCURSION,
|
||||
TYPE_READING to COLOR_READING,
|
||||
TYPE_CLASS_EVENT to COLOR_CLASS_EVENT,
|
||||
TYPE_INFORMATION to COLOR_INFORMATION
|
||||
)
|
||||
|
||||
fun getTypeNameMap() = mapOf(
|
||||
TYPE_ELEARNING to R.string.event_type_elearning,
|
||||
TYPE_HOMEWORK to R.string.event_type_homework,
|
||||
TYPE_DEFAULT to R.string.event_other,
|
||||
TYPE_EXAM to R.string.event_exam,
|
||||
TYPE_SHORT_QUIZ to R.string.event_short_quiz,
|
||||
TYPE_ESSAY to R.string.event_essay,
|
||||
TYPE_PROJECT to R.string.event_project,
|
||||
TYPE_PT_MEETING to R.string.event_pt_meeting,
|
||||
TYPE_EXCURSION to R.string.event_excursion,
|
||||
TYPE_READING to R.string.event_reading,
|
||||
TYPE_CLASS_EVENT to R.string.event_class_event,
|
||||
TYPE_INFORMATION to R.string.event_information
|
||||
)
|
||||
}
|
||||
}
|
@ -10,6 +10,8 @@ import android.content.Intent
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.google.gson.JsonObject
|
||||
import com.mikepenz.iconics.typeface.IIcon
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
|
||||
@Entity(tableName = "notifications")
|
||||
@ -19,6 +21,7 @@ data class Notification(
|
||||
|
||||
val title: String,
|
||||
val text: String,
|
||||
val textLong: String? = null,
|
||||
|
||||
val type: Int,
|
||||
|
||||
@ -96,4 +99,19 @@ data class Notification(
|
||||
fillIntent(intent)
|
||||
return PendingIntent.getActivity(context, id.toInt(), intent, PendingIntent.FLAG_ONE_SHOT)
|
||||
}
|
||||
|
||||
fun getLargeIcon(): IIcon = when (type) {
|
||||
TYPE_TIMETABLE_LESSON_CHANGE -> CommunityMaterial.Icon3.cmd_timetable
|
||||
TYPE_NEW_GRADE -> CommunityMaterial.Icon3.cmd_numeric_5_box_outline
|
||||
TYPE_NEW_EVENT -> CommunityMaterial.Icon.cmd_calendar_outline
|
||||
TYPE_NEW_HOMEWORK -> CommunityMaterial.Icon3.cmd_notebook_outline
|
||||
TYPE_NEW_SHARED_EVENT -> CommunityMaterial.Icon.cmd_calendar_outline
|
||||
TYPE_NEW_SHARED_HOMEWORK -> CommunityMaterial.Icon3.cmd_notebook_outline
|
||||
TYPE_NEW_MESSAGE -> CommunityMaterial.Icon.cmd_email_outline
|
||||
TYPE_NEW_NOTICE -> CommunityMaterial.Icon.cmd_emoticon_outline
|
||||
TYPE_NEW_ATTENDANCE -> CommunityMaterial.Icon.cmd_calendar_remove_outline
|
||||
TYPE_LUCKY_NUMBER -> CommunityMaterial.Icon.cmd_emoticon_excited_outline
|
||||
TYPE_NEW_ANNOUNCEMENT -> CommunityMaterial.Icon.cmd_bullhorn_outline
|
||||
else -> CommunityMaterial.Icon.cmd_bell_ring_outline
|
||||
}
|
||||
}
|
||||
|
@ -129,6 +129,9 @@ open class Profile(
|
||||
val isParent
|
||||
get() = accountName != null
|
||||
|
||||
val accountOwnerName
|
||||
get() = accountName ?: studentNameLong
|
||||
|
||||
val registerName
|
||||
get() = when (loginStoreType) {
|
||||
LOGIN_TYPE_LIBRUS -> "librus"
|
||||
|
@ -30,7 +30,7 @@ class MessageFull(
|
||||
@Ignore
|
||||
var filterWeight = 0
|
||||
@Ignore
|
||||
var searchHighlightText: String? = null
|
||||
var searchHighlightText: CharSequence? = null
|
||||
|
||||
// metadata
|
||||
var seen = false
|
||||
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-4-15.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.migration
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_ELEARNING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_ELEARNING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_INFORMATION
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.EventType.Companion.SOURCE_DEFAULT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.EventType.Companion.SOURCE_REGISTER
|
||||
import pl.szczodrzynski.edziennik.getInt
|
||||
|
||||
class Migration92 : Migration(91, 92) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// make eventTypeName not nullable
|
||||
database.execSQL("ALTER TABLE eventTypes RENAME TO _eventTypes;")
|
||||
database.execSQL("CREATE TABLE eventTypes (" +
|
||||
"profileId INTEGER NOT NULL, " +
|
||||
"eventType INTEGER NOT NULL, " +
|
||||
"eventTypeName TEXT NOT NULL, " +
|
||||
"eventTypeColor INTEGER NOT NULL, " +
|
||||
"PRIMARY KEY(profileId,eventType)" +
|
||||
");")
|
||||
database.execSQL("INSERT INTO eventTypes " +
|
||||
"(profileId, eventType, eventTypeName, eventTypeColor) " +
|
||||
"SELECT profileId, eventType, eventTypeName, eventTypeColor " +
|
||||
"FROM _eventTypes;")
|
||||
database.execSQL("DROP TABLE _eventTypes;")
|
||||
|
||||
// add columns for order and source
|
||||
database.execSQL("ALTER TABLE eventTypes ADD COLUMN eventTypeOrder INTEGER NOT NULL DEFAULT 0;")
|
||||
database.execSQL("ALTER TABLE eventTypes ADD COLUMN eventTypeSource INTEGER NOT NULL DEFAULT 0;")
|
||||
|
||||
// migrate existing types to show correct order and source
|
||||
database.execSQL("UPDATE eventTypes SET eventTypeOrder = eventType + 102;")
|
||||
database.execSQL("UPDATE eventTypes SET eventTypeSource = $SOURCE_REGISTER WHERE eventType > $TYPE_INFORMATION;")
|
||||
|
||||
// add new e-learning type
|
||||
val cursor = database.query("SELECT profileId FROM profiles;")
|
||||
cursor.use {
|
||||
while (it.moveToNext()) {
|
||||
val values = ContentValues().apply {
|
||||
put("profileId", it.getInt("profileId"))
|
||||
put("eventType", TYPE_ELEARNING)
|
||||
put("eventTypeName", "lekcja online")
|
||||
put("eventTypeColor", COLOR_ELEARNING)
|
||||
put("eventTypeOrder", 100)
|
||||
put("eventTypeSource", SOURCE_DEFAULT)
|
||||
}
|
||||
|
||||
database.insert("eventTypes", SQLiteDatabase.CONFLICT_REPLACE, values)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-5-26.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.migration
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration93 : Migration(92, 93) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// notifications - long text
|
||||
database.execSQL("ALTER TABLE notifications ADD COLUMN textLong TEXT DEFAULT NULL;")
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-4-14.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.dialogs
|
||||
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.databinding.MessagesConfigDialogBinding
|
||||
|
||||
class MessagesConfigDialog(
|
||||
private val activity: AppCompatActivity,
|
||||
private val reloadOnDismiss: Boolean = true,
|
||||
private val onShowListener: ((tag: String) -> Unit)? = null,
|
||||
private val onDismissListener: ((tag: String) -> Unit)? = null
|
||||
) {
|
||||
companion object {
|
||||
const val TAG = "MessagesConfigDialog"
|
||||
}
|
||||
|
||||
private val app by lazy { activity.application as App }
|
||||
private val config by lazy { app.config.ui }
|
||||
private val profileConfig by lazy { app.config.forProfile().ui }
|
||||
|
||||
private lateinit var b: MessagesConfigDialogBinding
|
||||
private lateinit var dialog: AlertDialog
|
||||
|
||||
init { run {
|
||||
if (activity.isFinishing)
|
||||
return@run
|
||||
b = MessagesConfigDialogBinding.inflate(activity.layoutInflater)
|
||||
onShowListener?.invoke(TAG)
|
||||
dialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.menu_messages_config)
|
||||
.setView(b.root)
|
||||
.setPositiveButton(R.string.ok) { dialog, _ -> dialog.dismiss() }
|
||||
.setOnDismissListener {
|
||||
saveConfig()
|
||||
onDismissListener?.invoke(TAG)
|
||||
if (reloadOnDismiss) (activity as? MainActivity)?.reloadTarget()
|
||||
}
|
||||
.create()
|
||||
loadConfig()
|
||||
dialog.show()
|
||||
}}
|
||||
|
||||
private fun loadConfig() {
|
||||
b.config = profileConfig
|
||||
|
||||
b.greetingText.setText(
|
||||
profileConfig.messagesGreetingText ?: "\n\nZ poważaniem\n${app.profile.accountOwnerName}"
|
||||
)
|
||||
}
|
||||
|
||||
private fun saveConfig() {
|
||||
val greetingText = b.greetingText.text?.toString()?.trim()
|
||||
if (greetingText.isNullOrEmpty())
|
||||
profileConfig.messagesGreetingText = null
|
||||
else
|
||||
profileConfig.messagesGreetingText = "\n\n$greetingText"
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-4-10.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.dialogs.agenda
|
||||
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.REGISTRATION_ENABLED
|
||||
import pl.szczodrzynski.edziennik.databinding.DialogConfigAgendaBinding
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.sync.RegistrationConfigDialog
|
||||
import java.util.*
|
||||
|
||||
class AgendaConfigDialog(
|
||||
private val activity: AppCompatActivity,
|
||||
private val reloadOnDismiss: Boolean = true,
|
||||
private val onShowListener: ((tag: String) -> Unit)? = null,
|
||||
private val onDismissListener: ((tag: String) -> Unit)? = null
|
||||
) {
|
||||
companion object {
|
||||
const val TAG = "AgendaConfigDialog"
|
||||
}
|
||||
|
||||
private val app by lazy { activity.application as App }
|
||||
private val config by lazy { app.config.ui }
|
||||
private val profileConfig by lazy { app.config.forProfile().ui }
|
||||
|
||||
private lateinit var b: DialogConfigAgendaBinding
|
||||
private lateinit var dialog: AlertDialog
|
||||
|
||||
init { run {
|
||||
if (activity.isFinishing)
|
||||
return@run
|
||||
b = DialogConfigAgendaBinding.inflate(activity.layoutInflater)
|
||||
onShowListener?.invoke(TAG)
|
||||
dialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.menu_agenda_config)
|
||||
.setView(b.root)
|
||||
.setPositiveButton(R.string.ok) { dialog, _ -> dialog.dismiss() }
|
||||
.setOnDismissListener {
|
||||
saveConfig()
|
||||
onDismissListener?.invoke(TAG)
|
||||
if (reloadOnDismiss) (activity as? MainActivity)?.reloadTarget()
|
||||
}
|
||||
.create()
|
||||
loadConfig()
|
||||
dialog.show()
|
||||
}}
|
||||
|
||||
private fun loadConfig() {
|
||||
b.config = profileConfig
|
||||
b.isAgendaMode = profileConfig.agendaViewType == Profile.AGENDA_DEFAULT
|
||||
|
||||
b.eventSharingEnabled.isChecked = app.profile.enableSharedEvents
|
||||
&& app.profile.registration == REGISTRATION_ENABLED
|
||||
b.eventSharingEnabled.onChange { _, isChecked ->
|
||||
if (isChecked && app.profile.registration != REGISTRATION_ENABLED) {
|
||||
b.eventSharingEnabled.isChecked = false
|
||||
val dialog = RegistrationConfigDialog(activity, app.profile, onChangeListener = { enabled ->
|
||||
b.eventSharingEnabled.isChecked = enabled
|
||||
setEventSharingEnabled(enabled)
|
||||
}, onShowListener, onDismissListener)
|
||||
dialog.showEnableDialog()
|
||||
return@onChange
|
||||
}
|
||||
setEventSharingEnabled(isChecked)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setEventSharingEnabled(enabled: Boolean) {
|
||||
if (enabled == app.profile.enableSharedEvents)
|
||||
return
|
||||
app.profile.enableSharedEvents = enabled
|
||||
app.profileSave()
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.event_sharing)
|
||||
.setMessage(
|
||||
if (enabled)
|
||||
R.string.settings_register_shared_events_dialog_enabled_text
|
||||
else
|
||||
R.string.settings_register_shared_events_dialog_disabled_text
|
||||
)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun saveConfig() {
|
||||
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ package pl.szczodrzynski.edziennik.ui.dialogs.day
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.*
|
||||
@ -19,6 +19,10 @@ import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.lessonchange.LessonChangeDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.teacherabsence.TeacherAbsenceDialog
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchanges.LessonChangesEvent
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchanges.LessonChangesEventRenderer
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEvent
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEventRenderer
|
||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
@ -29,6 +33,7 @@ class DayDialog(
|
||||
val activity: AppCompatActivity,
|
||||
val profileId: Int,
|
||||
val date: Date,
|
||||
val eventTypeId: Long? = null,
|
||||
val onShowListener: ((tag: String) -> Unit)? = null,
|
||||
val onDismissListener: ((tag: String) -> Unit)? = null
|
||||
) : CoroutineScope {
|
||||
@ -109,10 +114,16 @@ class DayDialog(
|
||||
}
|
||||
|
||||
lessonChanges.ifNotEmpty {
|
||||
b.lessonChangeContainer.root.visibility = View.VISIBLE
|
||||
b.lessonChangeContainer.lessonChangeCount.text = it.size.toString()
|
||||
LessonChangesEventRenderer().render(
|
||||
b.lessonChanges, LessonChangesEvent(
|
||||
profileId = profileId,
|
||||
date = date,
|
||||
count = it.size,
|
||||
showBadge = false
|
||||
)
|
||||
)
|
||||
|
||||
b.lessonChangeLayout.onClick {
|
||||
b.lessonChangesFrame.onClick {
|
||||
LessonChangeDialog(
|
||||
activity,
|
||||
profileId,
|
||||
@ -122,16 +133,22 @@ class DayDialog(
|
||||
)
|
||||
}
|
||||
}
|
||||
b.lessonChangesFrame.isVisible = lessonChanges.isNotEmpty()
|
||||
|
||||
val teacherAbsences = withContext(Dispatchers.Default) {
|
||||
app.db.teacherAbsenceDao().getAllByDateNow(profileId, date)
|
||||
}
|
||||
|
||||
teacherAbsences.ifNotEmpty {
|
||||
b.teacherAbsenceContainer.root.visibility = View.VISIBLE
|
||||
b.teacherAbsenceContainer.teacherAbsenceCount.text = it.size.toString()
|
||||
TeacherAbsenceEventRenderer().render(
|
||||
b.teacherAbsence, TeacherAbsenceEvent(
|
||||
profileId = profileId,
|
||||
date = date,
|
||||
count = it.size
|
||||
)
|
||||
)
|
||||
|
||||
b.teacherAbsenceLayout.onClick {
|
||||
b.teacherAbsenceFrame.onClick {
|
||||
TeacherAbsenceDialog(
|
||||
activity,
|
||||
profileId,
|
||||
@ -141,6 +158,7 @@ class DayDialog(
|
||||
)
|
||||
}
|
||||
}
|
||||
b.teacherAbsenceFrame.isVisible = teacherAbsences.isNotEmpty()
|
||||
|
||||
adapter = EventListAdapter(
|
||||
activity,
|
||||
@ -169,8 +187,12 @@ class DayDialog(
|
||||
}
|
||||
)
|
||||
|
||||
app.db.eventDao().getAllByDate(profileId, date).observe(activity, Observer { events ->
|
||||
adapter.items = events
|
||||
app.db.eventDao().getAllByDate(profileId, date).observe(activity) { events ->
|
||||
adapter.items = if (eventTypeId != null)
|
||||
events.filter { it.type == eventTypeId }
|
||||
else
|
||||
events
|
||||
|
||||
if (b.eventsView.adapter == null) {
|
||||
b.eventsView.adapter = adapter
|
||||
b.eventsView.apply {
|
||||
@ -189,6 +211,6 @@ class DayDialog(
|
||||
b.eventsView.visibility = View.GONE
|
||||
b.eventsNoData.visibility = View.VISIBLE
|
||||
}
|
||||
})
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class EventDetailsDialog(
|
||||
val activity: AppCompatActivity,
|
||||
val event: EventFull,
|
||||
var event: EventFull,
|
||||
val onShowListener: ((tag: String) -> Unit)? = null,
|
||||
val onDismissListener: ((tag: String) -> Unit)? = null
|
||||
) : CoroutineScope {
|
||||
@ -46,6 +46,8 @@ class EventDetailsDialog(
|
||||
private var removeEventDialog: AlertDialog? = null
|
||||
private val eventShared = event.sharedBy != null
|
||||
private val eventOwn = event.sharedBy == "self"
|
||||
private val manager
|
||||
get() = app.eventManager
|
||||
|
||||
private val job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
@ -92,6 +94,10 @@ class EventDetailsDialog(
|
||||
b.eventShared = eventShared
|
||||
b.eventOwn = eventOwn
|
||||
|
||||
if (!event.seen) {
|
||||
manager.markAsSeen(event)
|
||||
}
|
||||
|
||||
val bullet = " • "
|
||||
val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(activity)
|
||||
|
||||
@ -100,6 +106,8 @@ class EventDetailsDialog(
|
||||
}
|
||||
catch (_: Exception) {}
|
||||
|
||||
manager.setLegendText(b.legend, event)
|
||||
|
||||
b.typeColor.background?.setTintColor(event.eventColor)
|
||||
|
||||
b.details = mutableListOf(
|
||||
@ -135,6 +143,7 @@ class EventDetailsDialog(
|
||||
launch(Dispatchers.Default) {
|
||||
app.db.eventDao().replace(event)
|
||||
}
|
||||
update()
|
||||
b.checkDoneButton.isChecked = true
|
||||
}
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
@ -145,6 +154,7 @@ class EventDetailsDialog(
|
||||
launch(Dispatchers.Default) {
|
||||
app.db.eventDao().replace(event)
|
||||
}
|
||||
update()
|
||||
}
|
||||
}
|
||||
b.checkDoneButton.attachToastHint(R.string.hint_mark_as_done)
|
||||
@ -156,6 +166,14 @@ class EventDetailsDialog(
|
||||
activity,
|
||||
event.profileId,
|
||||
editingEvent = event,
|
||||
onSaveListener = {
|
||||
if (it == null) {
|
||||
dialog.dismiss()
|
||||
return@EventManualDialog
|
||||
}
|
||||
event = it
|
||||
update()
|
||||
},
|
||||
onShowListener = onShowListener,
|
||||
onDismissListener = onDismissListener
|
||||
)
|
||||
@ -199,10 +217,14 @@ class EventDetailsDialog(
|
||||
}
|
||||
b.downloadButton.attachToastHint(R.string.hint_download_again)
|
||||
|
||||
BetterLink.attach(b.topic, onActionSelected = dialog::dismiss)
|
||||
|
||||
b.topic.text = event.topic
|
||||
BetterLink.attach(b.topic) {
|
||||
dialog.dismiss()
|
||||
event.teacherName?.let { name ->
|
||||
BetterLink.attach(
|
||||
b.teacherName,
|
||||
teachers = mapOf(event.teacherId to name),
|
||||
onActionSelected = dialog::dismiss
|
||||
)
|
||||
}
|
||||
|
||||
if (event.homeworkBody == null && !event.addedManually && event.type == Event.TYPE_HOMEWORK) {
|
||||
@ -220,10 +242,7 @@ class EventDetailsDialog(
|
||||
b.bodyTitle.isVisible = true
|
||||
b.bodyProgressBar.isVisible = false
|
||||
b.body.isVisible = true
|
||||
b.body.text = event.homeworkBody
|
||||
BetterLink.attach(b.body) {
|
||||
dialog.dismiss()
|
||||
}
|
||||
BetterLink.attach(b.body, onActionSelected = dialog::dismiss)
|
||||
}
|
||||
|
||||
if (event.attachmentIds.isNullOrEmpty() || event.attachmentNames.isNullOrEmpty()) {
|
||||
@ -322,8 +341,6 @@ class EventDetailsDialog(
|
||||
removeEventDialog?.dismiss()
|
||||
dialog.dismiss()
|
||||
Toast.makeText(activity, R.string.removed, Toast.LENGTH_SHORT).show()
|
||||
if (activity is MainActivity && activity.navTargetId == MainActivity.DRAWER_ITEM_AGENDA)
|
||||
activity.reloadTarget()
|
||||
}
|
||||
|
||||
private fun openInCalendar() { launch {
|
||||
|
@ -33,7 +33,8 @@ class EventListAdapter(
|
||||
) : RecyclerView.Adapter<EventListAdapter.ViewHolder>(), CoroutineScope {
|
||||
|
||||
private val app = context.applicationContext as App
|
||||
private val manager = app.eventManager
|
||||
private val manager
|
||||
get() = app.eventManager
|
||||
|
||||
private val job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
@ -67,7 +68,7 @@ class EventListAdapter(
|
||||
|
||||
b.simpleMode = simpleMode
|
||||
|
||||
b.topic.text = event.topic
|
||||
manager.setEventTopic(b.topic, event, showType = false)
|
||||
b.topic.maxLines = if (simpleMode) 2 else 3
|
||||
|
||||
b.details.text = mutableListOf<CharSequence?>(
|
||||
@ -102,8 +103,6 @@ class EventListAdapter(
|
||||
}
|
||||
b.editButton.attachToastHint(R.string.hint_edit_event)
|
||||
|
||||
b.isDone.isVisible = event.isDone
|
||||
|
||||
if (event.showAsUnseen == null)
|
||||
event.showAsUnseen = !event.seen
|
||||
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.dialogs.event
|
||||
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
@ -20,23 +18,18 @@ import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskAllFinishedEvent
|
||||
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskErrorEvent
|
||||
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskFinishedEvent
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.EventType
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.*
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
|
||||
import pl.szczodrzynski.edziennik.databinding.DialogEventManualV2Binding
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.sync.RegistrationConfigDialog
|
||||
import pl.szczodrzynski.edziennik.ui.modules.views.TimeDropdown.Companion.DISPLAY_LESSONS
|
||||
import pl.szczodrzynski.edziennik.utils.Anim
|
||||
import pl.szczodrzynski.edziennik.utils.TextInputDropDown
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
@ -49,6 +42,7 @@ class EventManualDialog(
|
||||
val defaultTime: Time? = null,
|
||||
val defaultType: Long? = null,
|
||||
val editingEvent: EventFull? = null,
|
||||
val onSaveListener: ((event: EventFull?) -> Unit)? = null,
|
||||
val onShowListener: ((tag: String) -> Unit)? = null,
|
||||
val onDismissListener: ((tag: String) -> Unit)? = null
|
||||
) : CoroutineScope {
|
||||
@ -323,57 +317,41 @@ class EventManualDialog(
|
||||
selectDefault(defaultLesson?.displayTeacherId)
|
||||
}
|
||||
|
||||
with (b.typeDropdown) {
|
||||
db = app.db
|
||||
profileId = this@EventManualDialog.profileId
|
||||
loadItems()
|
||||
selectDefault(editingEvent?.type)
|
||||
selectDefault(defaultType)
|
||||
|
||||
val deferred = async(Dispatchers.Default) {
|
||||
// get the event type list
|
||||
var eventTypes = app.db.eventTypeDao().getAllNow(profileId)
|
||||
|
||||
if (eventTypes.none { it.id in -1L..10L }) {
|
||||
eventTypes = app.db.eventTypeDao().addDefaultTypes(activity, profileId)
|
||||
onTypeSelected = {
|
||||
b.typeColor.background.setTintColor(it.color)
|
||||
customColor = null
|
||||
}
|
||||
}
|
||||
|
||||
b.typeDropdown.clear()
|
||||
b.typeDropdown += eventTypes.map { TextInputDropDown.Item(it.id, it.name, tag = it) }
|
||||
}
|
||||
deferred.await()
|
||||
|
||||
b.typeDropdown.isEnabled = true
|
||||
|
||||
defaultType?.let {
|
||||
b.typeDropdown.select(it)
|
||||
}
|
||||
|
||||
b.typeDropdown.selected?.let { item ->
|
||||
customColor = (item.tag as EventType).color
|
||||
}
|
||||
|
||||
// copy IDs from event being edited
|
||||
// copy data from event being edited
|
||||
editingEvent?.let {
|
||||
b.topic.setText(it.topic)
|
||||
b.typeDropdown.select(it.type)?.let { item ->
|
||||
customColor = (item.tag as EventType).color
|
||||
}
|
||||
if (it.color != null && it.color != -1)
|
||||
if (it.color != -1)
|
||||
customColor = it.color
|
||||
}
|
||||
|
||||
b.typeColor.background.setTintColor(
|
||||
customColor
|
||||
?: b.typeDropdown.getSelected()?.color
|
||||
?: Event.COLOR_DEFAULT
|
||||
)
|
||||
|
||||
// copy IDs from the LessonFull
|
||||
defaultLesson?.let {
|
||||
b.teamDropdown.select(it.displayTeamId)
|
||||
}
|
||||
|
||||
b.typeDropdown.setOnChangeListener {
|
||||
b.typeColor.background.colorFilter = PorterDuffColorFilter((it.tag as EventType).color, PorterDuff.Mode.SRC_ATOP)
|
||||
customColor = null
|
||||
return@setOnChangeListener true
|
||||
}
|
||||
|
||||
(customColor ?: Event.COLOR_DEFAULT).let {
|
||||
b.typeColor.background.colorFilter = PorterDuffColorFilter(it, PorterDuff.Mode.SRC_ATOP)
|
||||
}
|
||||
|
||||
b.typeColor.onClick {
|
||||
val currentColor = (b.typeDropdown.selected?.tag as EventType?)?.color ?: Event.COLOR_DEFAULT
|
||||
val currentColor = customColor
|
||||
?: b.typeDropdown.getSelected()?.color
|
||||
?: Event.COLOR_DEFAULT
|
||||
val colorPickerDialog = ColorPickerDialog.newBuilder()
|
||||
.setColor(currentColor)
|
||||
.create()
|
||||
@ -381,7 +359,7 @@ class EventManualDialog(
|
||||
object : ColorPickerDialogListener {
|
||||
override fun onDialogDismissed(dialogId: Int) {}
|
||||
override fun onColorSelected(dialogId: Int, color: Int) {
|
||||
b.typeColor.background.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP)
|
||||
b.typeColor.background.setTintColor(color)
|
||||
customColor = color
|
||||
}
|
||||
})
|
||||
@ -416,11 +394,11 @@ class EventManualDialog(
|
||||
private fun saveEvent() {
|
||||
val date = b.dateDropdown.getSelected() as? Date
|
||||
val timeSelected = b.timeDropdown.getSelected()
|
||||
val teamId = b.teamDropdown.getSelected() as? Long
|
||||
val type = b.typeDropdown.selected?.id
|
||||
val team = b.teamDropdown.getSelected()
|
||||
val type = b.typeDropdown.getSelected()
|
||||
val topic = b.topic.text?.toString()
|
||||
val subjectId = b.subjectDropdown.getSelected() as? Long
|
||||
val teacherId = b.teacherDropdown.getSelected()
|
||||
val subject = b.subjectDropdown.getSelected() as? Subject
|
||||
val teacher = b.teacherDropdown.getSelected()
|
||||
|
||||
val share = b.shareSwitch.isChecked
|
||||
|
||||
@ -451,7 +429,7 @@ class EventManualDialog(
|
||||
isError = true
|
||||
}
|
||||
|
||||
if (share && teamId == null) {
|
||||
if (share && team == null) {
|
||||
b.teamDropdown.error = app.getString(R.string.dialog_event_manual_team_choose)
|
||||
if (!isError) b.teamDropdown.parent.requestChildFocus(b.teamDropdown, b.teamDropdown)
|
||||
isError = true
|
||||
@ -487,10 +465,10 @@ class EventManualDialog(
|
||||
time = startTime,
|
||||
topic = topic,
|
||||
color = customColor,
|
||||
type = type ?: Event.TYPE_DEFAULT,
|
||||
teacherId = teacherId ?: -1,
|
||||
subjectId = subjectId ?: -1,
|
||||
teamId = teamId ?: -1,
|
||||
type = type?.id ?: Event.TYPE_DEFAULT,
|
||||
teacherId = teacher?.id ?: -1,
|
||||
subjectId = subject?.id ?: -1,
|
||||
teamId = team?.id ?: -1,
|
||||
addedDate = editingEvent?.addedDate ?: System.currentTimeMillis()
|
||||
).also {
|
||||
it.addedManually = true
|
||||
@ -498,7 +476,7 @@ class EventManualDialog(
|
||||
|
||||
val metadataObject = Metadata(
|
||||
profileId,
|
||||
when (type) {
|
||||
when (type?.id) {
|
||||
Event.TYPE_HOMEWORK -> Metadata.TYPE_HOMEWORK
|
||||
else -> Metadata.TYPE_EVENT
|
||||
},
|
||||
@ -597,10 +575,14 @@ class EventManualDialog(
|
||||
}
|
||||
}
|
||||
|
||||
onSaveListener?.invoke(eventObject.withMetadata(metadataObject).also {
|
||||
it.subjectLongName = (b.subjectDropdown.getSelected() as? Subject)?.longName
|
||||
it.teacherName = b.teacherDropdown.getSelected()?.fullName
|
||||
it.teamName = b.teamDropdown.getSelected()?.name
|
||||
it.typeName = b.typeDropdown.getSelected()?.name
|
||||
})
|
||||
dialog.dismiss()
|
||||
Toast.makeText(activity, R.string.saved, Toast.LENGTH_SHORT).show()
|
||||
if (activity is MainActivity && activity.navTargetId == DRAWER_ITEM_AGENDA)
|
||||
activity.reloadTarget()
|
||||
}
|
||||
private fun finishRemoving() {
|
||||
editingEvent ?: return
|
||||
@ -611,9 +593,8 @@ class EventManualDialog(
|
||||
}
|
||||
|
||||
removeEventDialog?.dismiss()
|
||||
onSaveListener?.invoke(null)
|
||||
dialog.dismiss()
|
||||
Toast.makeText(activity, R.string.removed, Toast.LENGTH_SHORT).show()
|
||||
if (activity is MainActivity && activity.navTargetId == DRAWER_ITEM_AGENDA)
|
||||
activity.reloadTarget()
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import pl.szczodrzynski.edziennik.databinding.DialogGradeDetailsBinding
|
||||
import pl.szczodrzynski.edziennik.onClick
|
||||
import pl.szczodrzynski.edziennik.setTintColor
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter
|
||||
import pl.szczodrzynski.edziennik.utils.BetterLink
|
||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
@ -68,6 +69,14 @@ class GradeDetailsDialog(
|
||||
GradesConfigDialog(activity, reloadOnDismiss = true)
|
||||
}
|
||||
|
||||
grade.teacherName?.let { name ->
|
||||
BetterLink.attach(
|
||||
b.teacherName,
|
||||
teachers = mapOf(grade.teacherId to name),
|
||||
onActionSelected = dialog::dismiss
|
||||
)
|
||||
}
|
||||
|
||||
launch {
|
||||
val historyList = withContext(Dispatchers.Default) {
|
||||
app.db.gradeDao().getByParentIdNow(App.profileId, grade.id)
|
||||
|
@ -25,6 +25,7 @@ import pl.szczodrzynski.edziennik.ui.dialogs.event.EventDetailsDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
|
||||
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
|
||||
import pl.szczodrzynski.edziennik.utils.BetterLink
|
||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||
@ -49,7 +50,8 @@ class LessonDetailsDialog(
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
private lateinit var adapter: EventListAdapter
|
||||
private val manager by lazy { app.timetableManager }
|
||||
private val manager
|
||||
get() = app.timetableManager
|
||||
|
||||
init { run {
|
||||
if (activity.isFinishing)
|
||||
@ -216,5 +218,19 @@ class LessonDetailsDialog(
|
||||
b.eventsNoData.visibility = View.VISIBLE
|
||||
}
|
||||
})
|
||||
|
||||
lesson.displayTeacherName?.let { name ->
|
||||
lesson.displayTeacherId ?: return@let
|
||||
BetterLink.attach(
|
||||
b.teacherNameView,
|
||||
teachers = mapOf(lesson.displayTeacherId!! to name),
|
||||
onActionSelected = dialog::dismiss
|
||||
)
|
||||
BetterLink.attach(
|
||||
b.oldTeacherNameView,
|
||||
teachers = mapOf(lesson.displayTeacherId!! to name),
|
||||
onActionSelected = dialog::dismiss
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,10 +12,6 @@ import android.widget.Toast
|
||||
import androidx.databinding.ViewDataBinding
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.applandeo.materialcalendarview.EventDay
|
||||
import com.github.tibolte.agendacalendarview.CalendarPickerController
|
||||
import com.github.tibolte.agendacalendarview.models.BaseCalendarEvent
|
||||
import com.github.tibolte.agendacalendarview.models.CalendarEvent
|
||||
import com.github.tibolte.agendacalendarview.models.IDayItem
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
@ -29,17 +25,9 @@ import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaCalendarBinding
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaDefaultBinding
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.agenda.AgendaConfigDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.day.DayDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.lessonchange.LessonChangeDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.teacherabsence.TeacherAbsenceDialog
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchange.LessonChangeCounter
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchange.LessonChangeEvent
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchange.LessonChangeEventRenderer
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceCounter
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEvent
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEventRenderer
|
||||
import pl.szczodrzynski.edziennik.utils.Colors
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
||||
@ -59,7 +47,8 @@ class AgendaFragment : Fragment(), CoroutineScope {
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
private var type: Int = Profile.AGENDA_DEFAULT
|
||||
private var actualDate: Date? = null
|
||||
|
||||
private var agendaDefault: AgendaFragmentDefault? = null
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
if (getActivity() == null || context == null) return null
|
||||
@ -82,38 +71,61 @@ class AgendaFragment : Fragment(), CoroutineScope {
|
||||
.withTitle(R.string.menu_add_event)
|
||||
.withDescription(R.string.menu_add_event_desc)
|
||||
.withIcon(SzkolnyFont.Icon.szf_calendar_plus_outline)
|
||||
.withOnClickListener(View.OnClickListener {
|
||||
.withOnClickListener {
|
||||
activity.bottomSheet.close()
|
||||
EventManualDialog(activity, app.profileId, defaultDate = actualDate)
|
||||
}),
|
||||
EventManualDialog(
|
||||
activity,
|
||||
app.profileId,
|
||||
defaultDate = AgendaFragmentDefault.selectedDate
|
||||
)
|
||||
},
|
||||
BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_agenda_config)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_cog_outline)
|
||||
.withOnClickListener {
|
||||
activity.bottomSheet.close()
|
||||
AgendaConfigDialog(activity, true, null, null)
|
||||
},
|
||||
BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_agenda_change_view)
|
||||
.withIcon(if (type == Profile.AGENDA_DEFAULT) CommunityMaterial.Icon.cmd_calendar_outline else CommunityMaterial.Icon2.cmd_format_list_bulleted_square)
|
||||
.withOnClickListener(View.OnClickListener {
|
||||
.withOnClickListener {
|
||||
activity.bottomSheet.close()
|
||||
type = if (type == Profile.AGENDA_DEFAULT) Profile.AGENDA_CALENDAR else Profile.AGENDA_DEFAULT
|
||||
type =
|
||||
if (type == Profile.AGENDA_DEFAULT) Profile.AGENDA_CALENDAR else Profile.AGENDA_DEFAULT
|
||||
app.config.forProfile().ui.agendaViewType = type
|
||||
activity.reloadTarget()
|
||||
}),
|
||||
},
|
||||
BottomSheetSeparatorItem(true),
|
||||
BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_mark_as_read)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
|
||||
.withOnClickListener(View.OnClickListener { launch {
|
||||
.withOnClickListener {
|
||||
launch {
|
||||
activity.bottomSheet.close()
|
||||
withContext(Dispatchers.Default) {
|
||||
App.db.metadataDao().setAllSeen(app.profileId, Metadata.TYPE_EVENT, true)
|
||||
App.db.metadataDao()
|
||||
.setAllSeen(app.profileId, Metadata.TYPE_EVENT, true)
|
||||
}
|
||||
Toast.makeText(
|
||||
activity,
|
||||
R.string.main_menu_mark_as_read_success,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show()
|
||||
}})
|
||||
)
|
||||
|
||||
activity.navView.bottomBar.fabEnable = true
|
||||
activity.navView.bottomBar.fabExtendedText = getString(R.string.add)
|
||||
activity.navView.bottomBar.fabIcon = CommunityMaterial.Icon3.cmd_plus
|
||||
activity.navView.setFabOnClickListener(View.OnClickListener {
|
||||
EventManualDialog(activity, app.profileId, defaultDate = actualDate)
|
||||
})
|
||||
activity.navView.setFabOnClickListener {
|
||||
EventManualDialog(
|
||||
activity,
|
||||
app.profileId,
|
||||
defaultDate = AgendaFragmentDefault.selectedDate
|
||||
)
|
||||
}
|
||||
|
||||
activity.gainAttention()
|
||||
activity.gainAttentionFAB()
|
||||
@ -129,143 +141,8 @@ class AgendaFragment : Fragment(), CoroutineScope {
|
||||
return@launch
|
||||
delay(500)
|
||||
|
||||
val eventList = mutableListOf<CalendarEvent>()
|
||||
|
||||
val minDate = Calendar.getInstance().apply {
|
||||
add(Calendar.MONTH, -2)
|
||||
set(Calendar.DAY_OF_MONTH, 1)
|
||||
}
|
||||
val maxDate = Calendar.getInstance().apply { add(Calendar.MONTH, 2) }
|
||||
|
||||
/**
|
||||
* LESSON CHANGES
|
||||
*/
|
||||
if (!isAdded)
|
||||
return@launch
|
||||
|
||||
val lessons = withContext(Dispatchers.Default) { app.db.timetableDao().getChangesNow(app.profileId) }
|
||||
val lessonChangeCounters = mutableListOf<LessonChangeCounter>()
|
||||
|
||||
lessons.forEach { lesson ->
|
||||
lessonChangeCounters.firstOrNull { it.lessonChangeDate == lesson.displayDate }?.let {
|
||||
it.lessonChangeCount += 1
|
||||
} ?: run {
|
||||
lessonChangeCounters.add(LessonChangeCounter(
|
||||
lesson.displayDate ?: return@forEach,
|
||||
1
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
lessonChangeCounters.forEach { counter ->
|
||||
eventList.add(LessonChangeEvent(
|
||||
counter.lessonChangeDate.inMillis,
|
||||
0xff78909c.toInt(),
|
||||
Colors.legibleTextColor(0xff78909c.toInt()),
|
||||
counter.startTime,
|
||||
counter.endTime,
|
||||
app.profileId,
|
||||
counter.lessonChangeDate,
|
||||
counter.lessonChangeCount
|
||||
))
|
||||
}
|
||||
|
||||
/**
|
||||
* TEACHER ABSENCES
|
||||
*/
|
||||
if (!isAdded)
|
||||
return@launch
|
||||
|
||||
val showTeacherAbsences = app.profile.getStudentData("showTeacherAbsences", true)
|
||||
|
||||
if (showTeacherAbsences) {
|
||||
val teacherAbsenceList = withContext(Dispatchers.Default) { app.db.teacherAbsenceDao().getAllNow(app.profileId) }
|
||||
val teacherAbsenceCounters = mutableListOf<TeacherAbsenceCounter>()
|
||||
|
||||
teacherAbsenceList.forEach { absence ->
|
||||
val date = absence.dateFrom.clone()
|
||||
|
||||
while (date <= absence.dateTo) {
|
||||
teacherAbsenceCounters.firstOrNull { it.teacherAbsenceDate == date }?.let {
|
||||
it.teacherAbsenceCount += 1
|
||||
} ?: run {
|
||||
teacherAbsenceCounters.add(TeacherAbsenceCounter(date.clone(), 1))
|
||||
}
|
||||
|
||||
date.stepForward(0, 0, 1)
|
||||
}
|
||||
}
|
||||
|
||||
teacherAbsenceCounters.forEach { counter ->
|
||||
eventList.add(TeacherAbsenceEvent(
|
||||
counter.teacherAbsenceDate.inMillis,
|
||||
0xffff1744.toInt(),
|
||||
Colors.legibleTextColor(0xffff1744.toInt()),
|
||||
counter.startTime,
|
||||
counter.endTime,
|
||||
app.profileId,
|
||||
counter.teacherAbsenceDate,
|
||||
counter.teacherAbsenceCount
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* EVENTS
|
||||
*/
|
||||
if (!isAdded)
|
||||
return@launch
|
||||
|
||||
val events = withContext(Dispatchers.Default) { app.db.eventDao().getAllNow(app.profileId) }
|
||||
val unreadEventDates = mutableSetOf<Int>()
|
||||
|
||||
events.forEach { event ->
|
||||
eventList.add(BaseCalendarEvent(
|
||||
"${event.typeName ?: "wydarzenie"} - ${event.topic}",
|
||||
"",
|
||||
(if (event.time == null) getString(R.string.agenda_event_all_day) else event.time!!.stringHM) +
|
||||
(event.subjectLongName?.let { ", $it" } ?: "") +
|
||||
(event.teacherName?.let { ", $it" } ?: "") +
|
||||
(event.teamName?.let { ", $it" } ?: ""),
|
||||
event.eventColor,
|
||||
Colors.legibleTextColor(event.eventColor),
|
||||
event.startTimeCalendar,
|
||||
event.endTimeCalendar,
|
||||
event.time == null,
|
||||
event.id,
|
||||
!event.seen
|
||||
))
|
||||
|
||||
if (!event.seen) unreadEventDates.add(event.date.value)
|
||||
}
|
||||
|
||||
b.agendaDefaultView.init(eventList, minDate, maxDate, Locale.getDefault(), object : CalendarPickerController {
|
||||
override fun onDaySelected(dayItem: IDayItem?) {}
|
||||
|
||||
override fun onScrollToDate(calendar: Calendar) { this@AgendaFragment.launch {
|
||||
val date = Date.fromCalendar(calendar)
|
||||
actualDate = date
|
||||
|
||||
// Mark as read scrolled date
|
||||
if (date.value in unreadEventDates) {
|
||||
withContext(Dispatchers.Default) { app.db.eventDao().setSeenByDate(app.profileId, date, true) }
|
||||
unreadEventDates.remove(date.value)
|
||||
}
|
||||
}}
|
||||
|
||||
override fun onEventSelected(event: CalendarEvent) {
|
||||
val date = Date.fromCalendar(event.instanceDay)
|
||||
|
||||
when (event) {
|
||||
is BaseCalendarEvent -> DayDialog(activity, app.profileId, date)
|
||||
is LessonChangeEvent -> LessonChangeDialog(activity, app.profileId, date)
|
||||
is TeacherAbsenceEvent -> TeacherAbsenceDialog(activity, app.profileId, date)
|
||||
}
|
||||
}
|
||||
|
||||
}, LessonChangeEventRenderer(), TeacherAbsenceEventRenderer())
|
||||
|
||||
b.progressBar.visibility = View.GONE
|
||||
agendaDefault = AgendaFragmentDefault(activity, app, b)
|
||||
agendaDefault?.initView(this@AgendaFragment)
|
||||
}}}
|
||||
|
||||
private fun createCalendarAgendaView() { (b as? FragmentAgendaCalendarBinding)?.let { b -> launch {
|
||||
|
@ -0,0 +1,310 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-4-8.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.agenda
|
||||
|
||||
import android.util.SparseIntArray
|
||||
import android.widget.AbsListView
|
||||
import android.widget.AbsListView.OnScrollListener
|
||||
import androidx.core.util.forEach
|
||||
import androidx.core.util.set
|
||||
import androidx.core.view.isVisible
|
||||
import com.github.tibolte.agendacalendarview.CalendarManager
|
||||
import com.github.tibolte.agendacalendarview.CalendarPickerController
|
||||
import com.github.tibolte.agendacalendarview.agenda.AgendaAdapter
|
||||
import com.github.tibolte.agendacalendarview.models.BaseCalendarEvent
|
||||
import com.github.tibolte.agendacalendarview.models.CalendarEvent
|
||||
import com.github.tibolte.agendacalendarview.models.IDayItem
|
||||
import kotlinx.coroutines.*
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaDefaultBinding
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.day.DayDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventDetailsDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.lessonchange.LessonChangeDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.teacherabsence.TeacherAbsenceDialog
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.event.AgendaEvent
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.event.AgendaEventGroup
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.event.AgendaEventGroupRenderer
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.event.AgendaEventRenderer
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchanges.LessonChangesEvent
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchanges.LessonChangesEventRenderer
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEvent
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEventRenderer
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import java.util.*
|
||||
|
||||
class AgendaFragmentDefault(
|
||||
private val activity: MainActivity,
|
||||
private val app: App,
|
||||
private val b: FragmentAgendaDefaultBinding
|
||||
) : OnScrollListener, CoroutineScope {
|
||||
companion object {
|
||||
var selectedDate: Date = Date.getToday()
|
||||
}
|
||||
|
||||
override val coroutineContext = Job() + Dispatchers.Main
|
||||
|
||||
private val unreadDates = mutableSetOf<Int>()
|
||||
private val events = mutableListOf<CalendarEvent>()
|
||||
private var isInitialized = false
|
||||
private val profileConfig by lazy { app.config.forProfile().ui }
|
||||
|
||||
private val listView
|
||||
get() = b.agendaDefaultView.agendaView.agendaListView
|
||||
private val adapter
|
||||
get() = listView.adapter as? AgendaAdapter
|
||||
private val manager
|
||||
get() = CalendarManager.getInstance()
|
||||
|
||||
private var scrollState = OnScrollListener.SCROLL_STATE_IDLE
|
||||
private var updatePending = false
|
||||
private var notifyPending = false
|
||||
override fun onScrollStateChanged(view: AbsListView?, newScrollState: Int) {
|
||||
b.agendaDefaultView.agendaScrollListener.onScrollStateChanged(view, scrollState)
|
||||
scrollState = newScrollState
|
||||
if (updatePending) updateData()
|
||||
if (notifyPending) notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onScroll(
|
||||
view: AbsListView?,
|
||||
firstVisibleItem: Int,
|
||||
visibleItemCount: Int,
|
||||
totalItemCount: Int
|
||||
) = b.agendaDefaultView.agendaScrollListener.onScroll(
|
||||
view,
|
||||
firstVisibleItem,
|
||||
visibleItemCount,
|
||||
totalItemCount
|
||||
)
|
||||
|
||||
/**
|
||||
* Mark the data as needing update, either after 1 second (when
|
||||
* not scrolling) or 1 second after scrolling stops.
|
||||
*/
|
||||
private fun updateData() = launch {
|
||||
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) {
|
||||
updatePending = false
|
||||
delay(1000)
|
||||
notifyDataSetChanged()
|
||||
} else updatePending = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the adapter about changes, either instantly or after
|
||||
* scrolling stops.
|
||||
*/
|
||||
private fun notifyDataSetChanged() {
|
||||
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) {
|
||||
notifyPending = false
|
||||
adapter?.notifyDataSetChanged()
|
||||
} else notifyPending = true
|
||||
}
|
||||
|
||||
suspend fun initView(fragment: AgendaFragment) {
|
||||
isInitialized = false
|
||||
|
||||
withContext(Dispatchers.Default) {
|
||||
if (profileConfig.agendaLessonChanges)
|
||||
addLessonChanges(events)
|
||||
|
||||
if (profileConfig.agendaTeacherAbsence)
|
||||
addTeacherAbsence(events)
|
||||
}
|
||||
|
||||
app.db.eventDao().getAll(app.profileId).observe(fragment) {
|
||||
addEvents(events, it)
|
||||
if (isInitialized)
|
||||
updateView()
|
||||
else
|
||||
initViewPriv()
|
||||
}
|
||||
}
|
||||
|
||||
private fun initViewPriv() {
|
||||
val dateStart = app.profile.dateSemester1Start.asCalendar
|
||||
val dateEnd = app.profile.dateYearEnd.asCalendar
|
||||
|
||||
val isCompactMode = profileConfig.agendaCompactMode
|
||||
|
||||
b.agendaDefaultView.init(
|
||||
events,
|
||||
dateStart,
|
||||
dateEnd,
|
||||
Locale.getDefault(),
|
||||
object : CalendarPickerController {
|
||||
override fun onDaySelected(dayItem: IDayItem) {
|
||||
val c = Calendar.getInstance()
|
||||
c.time = dayItem.date
|
||||
if (c.timeInMillis == selectedDate.inMillis) {
|
||||
DayDialog(activity, app.profileId, selectedDate)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEventSelected(event: CalendarEvent) {
|
||||
val date = Date.fromCalendar(event.instanceDay)
|
||||
|
||||
when (event) {
|
||||
is AgendaEvent -> EventDetailsDialog(activity, event.event)
|
||||
is LessonChangesEvent -> LessonChangeDialog(activity, app.profileId, date)
|
||||
is TeacherAbsenceEvent -> TeacherAbsenceDialog(
|
||||
activity,
|
||||
app.profileId,
|
||||
date
|
||||
)
|
||||
is AgendaEventGroup -> DayDialog(activity, app.profileId, date, eventTypeId = event.typeId)
|
||||
is BaseCalendarEvent -> if (event.isPlaceHolder)
|
||||
DayDialog(activity, app.profileId, date)
|
||||
}
|
||||
|
||||
if (event is BaseEvent && event.showItemBadge) {
|
||||
val unreadCount = manager.events.count {
|
||||
it.instanceDay.equals(event.instanceDay) && it.showBadge
|
||||
}
|
||||
// only clicked event is unread, remove the day badge
|
||||
if (unreadCount == 1 && event.showBadge) {
|
||||
event.dayReference.showBadge = false
|
||||
unreadDates.remove(date.value)
|
||||
}
|
||||
setAsRead(event)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onScrollToDate(calendar: Calendar) {
|
||||
selectedDate = Date.fromCalendar(calendar)
|
||||
|
||||
// Mark as read scrolled date
|
||||
if (selectedDate.value in unreadDates) {
|
||||
setAsRead(calendar)
|
||||
activity.launch(Dispatchers.Default) {
|
||||
app.db.eventDao().setSeenByDate(app.profileId, selectedDate, true)
|
||||
}
|
||||
unreadDates.remove(selectedDate.value)
|
||||
}
|
||||
}
|
||||
},
|
||||
AgendaEventRenderer(app.eventManager, isCompactMode),
|
||||
AgendaEventGroupRenderer(),
|
||||
LessonChangesEventRenderer(),
|
||||
TeacherAbsenceEventRenderer()
|
||||
)
|
||||
|
||||
listView.setOnScrollListener(this)
|
||||
|
||||
isInitialized = true
|
||||
b.progressBar.isVisible = false
|
||||
}
|
||||
|
||||
private fun updateView() {
|
||||
manager.events.clear()
|
||||
manager.loadEvents(events, BaseCalendarEvent())
|
||||
|
||||
adapter?.updateEvents(manager.events)
|
||||
//listView.scrollToCurrentDate(selectedDate.asCalendar)
|
||||
}
|
||||
|
||||
private fun setAsRead(date: Calendar) {
|
||||
// get all events matching the date
|
||||
val events = manager.events.filter {
|
||||
if (it.instanceDay.equals(date) && it.showBadge && it is AgendaEvent) {
|
||||
// hide the day badge for the date
|
||||
it.dayReference.showBadge = false
|
||||
return@filter true
|
||||
}
|
||||
false
|
||||
}
|
||||
// set this date's events as read
|
||||
setAsRead(*events.toTypedArray())
|
||||
}
|
||||
|
||||
private fun setAsRead(vararg event: CalendarEvent) {
|
||||
// hide per-event badges
|
||||
for (e in event) {
|
||||
events.firstOrNull {
|
||||
it == e
|
||||
}?.showBadge = false
|
||||
e.showBadge = false
|
||||
}
|
||||
|
||||
listView.setOnScrollListener(this)
|
||||
updateData()
|
||||
}
|
||||
|
||||
private fun addEvents(
|
||||
events: MutableList<CalendarEvent>,
|
||||
eventList: List<EventFull>
|
||||
) {
|
||||
events.removeAll { it is AgendaEvent || it is AgendaEventGroup }
|
||||
|
||||
if (!profileConfig.agendaGroupByType) {
|
||||
events += eventList.map {
|
||||
if (!it.seen)
|
||||
unreadDates.add(it.date.value)
|
||||
AgendaEvent(it)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
eventList.groupBy {
|
||||
it.date.value to it.type
|
||||
}.forEach { (_, list) ->
|
||||
val event = list.first()
|
||||
if (list.size == 1) {
|
||||
if (!event.seen)
|
||||
unreadDates.add(event.date.value)
|
||||
events += AgendaEvent(event)
|
||||
} else {
|
||||
events.add(0, AgendaEventGroup(
|
||||
profileId = event.profileId,
|
||||
date = event.date,
|
||||
typeId = event.type,
|
||||
typeName = event.typeName ?: "-",
|
||||
typeColor = event.typeColor ?: event.eventColor,
|
||||
count = list.size,
|
||||
showBadge = list.any { !it.seen }
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addLessonChanges(events: MutableList<CalendarEvent>) {
|
||||
val lessons = app.db.timetableDao().getChangesNow(app.profileId)
|
||||
|
||||
val grouped = lessons.groupBy {
|
||||
it.displayDate
|
||||
}
|
||||
|
||||
events += grouped.mapNotNull { (date, changes) ->
|
||||
LessonChangesEvent(
|
||||
app.profileId,
|
||||
date = date ?: return@mapNotNull null,
|
||||
count = changes.size,
|
||||
showBadge = changes.any { !it.seen }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addTeacherAbsence(events: MutableList<CalendarEvent>) {
|
||||
val teacherAbsence = app.db.teacherAbsenceDao().getAllNow(app.profileId)
|
||||
|
||||
val countMap = SparseIntArray()
|
||||
|
||||
for (absence in teacherAbsence) {
|
||||
while (absence.dateFrom <= absence.dateTo) {
|
||||
countMap[absence.dateFrom.value] += 1
|
||||
absence.dateFrom.stepForward(0, 0, 1)
|
||||
}
|
||||
}
|
||||
|
||||
countMap.forEach { dateInt, count ->
|
||||
events += TeacherAbsenceEvent(
|
||||
app.profileId,
|
||||
date = Date.fromValue(dateInt),
|
||||
count = count
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-4-9.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.agenda
|
||||
|
||||
import com.github.tibolte.agendacalendarview.models.CalendarEvent
|
||||
import com.github.tibolte.agendacalendarview.models.IDayItem
|
||||
import com.github.tibolte.agendacalendarview.models.IWeekItem
|
||||
import java.util.*
|
||||
|
||||
open class BaseEvent(
|
||||
private val id: Long,
|
||||
private val time: Calendar,
|
||||
private val color: Int,
|
||||
private var showBadge: Boolean,
|
||||
var showItemBadge: Boolean = showBadge
|
||||
) : CalendarEvent {
|
||||
|
||||
override fun copy() = BaseEvent(id, time, color, showBadge)
|
||||
|
||||
private lateinit var date: Calendar
|
||||
override fun getInstanceDay() = date
|
||||
override fun setInstanceDay(value: Calendar) {
|
||||
date = value
|
||||
}
|
||||
|
||||
private lateinit var dayReference: IDayItem
|
||||
override fun getDayReference() = dayReference
|
||||
override fun setDayReference(value: IDayItem) {
|
||||
dayReference = value
|
||||
}
|
||||
|
||||
private lateinit var weekReference: IWeekItem
|
||||
override fun getWeekReference() = weekReference
|
||||
override fun setWeekReference(value: IWeekItem) {
|
||||
weekReference = value
|
||||
}
|
||||
|
||||
override fun getShowBadge() = showBadge
|
||||
override fun setShowBadge(value: Boolean) {
|
||||
showBadge = value
|
||||
showItemBadge = value
|
||||
}
|
||||
|
||||
override fun getId() = id
|
||||
override fun getStartTime() = time
|
||||
override fun getEndTime() = time
|
||||
override fun getTitle() = ""
|
||||
override fun getDescription() = ""
|
||||
override fun getLocation() = ""
|
||||
override fun getColor() = color
|
||||
override fun getTextColor() = 0
|
||||
override fun isPlaceholder() = false
|
||||
override fun isAllDay() = false
|
||||
|
||||
override fun setId(value: Long) = Unit
|
||||
override fun setStartTime(value: Calendar) = Unit
|
||||
override fun setEndTime(value: Calendar) = Unit
|
||||
override fun setTitle(value: String) = Unit
|
||||
override fun setDescription(value: String) = Unit
|
||||
override fun setLocation(value: String) = Unit
|
||||
override fun setTextColor(value: Int) = Unit
|
||||
override fun setPlaceholder(value: Boolean) = Unit
|
||||
override fun setAllDay(value: Boolean) = Unit
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-4-8.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.agenda.event
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.BaseEvent
|
||||
|
||||
class AgendaEvent(
|
||||
val event: EventFull,
|
||||
showBadge: Boolean = !event.seen
|
||||
) : BaseEvent(
|
||||
id = event.id,
|
||||
time = event.startTimeCalendar,
|
||||
color = event.eventColor,
|
||||
showBadge = showBadge
|
||||
) {
|
||||
override fun copy() = AgendaEvent(event, showBadge)
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-4-10.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.agenda.event
|
||||
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.BaseEvent
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class AgendaEventGroup(
|
||||
val profileId: Int,
|
||||
val date: Date,
|
||||
val typeId: Long,
|
||||
val typeName: String,
|
||||
val typeColor: Int,
|
||||
val count: Int,
|
||||
showBadge: Boolean
|
||||
) : BaseEvent(
|
||||
id = date.value.toLong(),
|
||||
time = date.asCalendar,
|
||||
color = typeColor,
|
||||
showBadge = showBadge
|
||||
) {
|
||||
override fun copy() = AgendaEventGroup(profileId, date, typeId, typeName, typeColor, count, showBadge)
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-4-10.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.agenda.event
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import com.github.tibolte.agendacalendarview.render.EventRenderer
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.databinding.AgendaWrappedGroupBinding
|
||||
import pl.szczodrzynski.edziennik.resolveAttr
|
||||
import pl.szczodrzynski.edziennik.setTintColor
|
||||
import pl.szczodrzynski.edziennik.utils.Colors
|
||||
|
||||
class AgendaEventGroupRenderer : EventRenderer<AgendaEventGroup>() {
|
||||
|
||||
override fun render(view: View, event: AgendaEventGroup) {
|
||||
val b = AgendaWrappedGroupBinding.bind(view).item
|
||||
|
||||
b.card.foreground.setTintColor(event.color)
|
||||
b.card.background.setTintColor(event.color)
|
||||
b.name.text = event.typeName
|
||||
b.name.setTextColor(Colors.legibleTextColor(event.color))
|
||||
b.count.text = event.count.toString()
|
||||
b.count.background.setTintColor(android.R.attr.colorBackground.resolveAttr(view.context))
|
||||
|
||||
b.badge.isVisible = event.showItemBadge
|
||||
}
|
||||
|
||||
override fun getEventLayout(): Int = R.layout.agenda_wrapped_group
|
||||
}
|
||||
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-4-8.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.agenda.event
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.isVisible
|
||||
import com.github.tibolte.agendacalendarview.render.EventRenderer
|
||||
import com.mikepenz.iconics.view.IconicsTextView
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.databinding.AgendaWrappedEventBinding
|
||||
import pl.szczodrzynski.edziennik.databinding.AgendaWrappedEventCompactBinding
|
||||
import pl.szczodrzynski.edziennik.join
|
||||
import pl.szczodrzynski.edziennik.resolveAttr
|
||||
import pl.szczodrzynski.edziennik.setTintColor
|
||||
import pl.szczodrzynski.edziennik.utils.Colors
|
||||
import pl.szczodrzynski.edziennik.utils.managers.EventManager
|
||||
|
||||
class AgendaEventRenderer(
|
||||
val manager: EventManager,
|
||||
val isCompact: Boolean
|
||||
) : EventRenderer<AgendaEvent>() {
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun render(view: View, aEvent: AgendaEvent) {
|
||||
if (isCompact) {
|
||||
val b = AgendaWrappedEventCompactBinding.bind(view).item
|
||||
bindView(aEvent, b.card, b.title, null, b.badgeBackground, b.badge)
|
||||
} else {
|
||||
val b = AgendaWrappedEventBinding.bind(view).item
|
||||
bindView(aEvent, b.card, b.title, b.subtitle, b.badgeBackground, b.badge)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindView(
|
||||
aEvent: AgendaEvent,
|
||||
card: FrameLayout,
|
||||
title: IconicsTextView,
|
||||
subtitle: TextView?,
|
||||
badgeBackground: View,
|
||||
badge: View
|
||||
) {
|
||||
val event = aEvent.event
|
||||
|
||||
val textColor = Colors.legibleTextColor(event.eventColor)
|
||||
|
||||
val timeText = if (event.time == null)
|
||||
card.context.getString(R.string.agenda_event_all_day)
|
||||
else
|
||||
event.time!!.stringHM
|
||||
|
||||
val eventSubtitle = listOfNotNull(
|
||||
timeText,
|
||||
event.subjectLongName,
|
||||
event.teacherName,
|
||||
event.teamName
|
||||
).join(", ")
|
||||
|
||||
card.foreground.setTintColor(event.eventColor)
|
||||
card.background.setTintColor(event.eventColor)
|
||||
manager.setEventTopic(title, event, doneIconColor = textColor)
|
||||
title.setTextColor(textColor)
|
||||
subtitle?.text = eventSubtitle
|
||||
subtitle?.setTextColor(textColor)
|
||||
|
||||
badgeBackground.isVisible = aEvent.showItemBadge
|
||||
badgeBackground.background.setTintColor(
|
||||
android.R.attr.colorBackground.resolveAttr(card.context)
|
||||
)
|
||||
badge.isVisible = aEvent.showItemBadge
|
||||
}
|
||||
|
||||
override fun getEventLayout() = if (isCompact)
|
||||
R.layout.agenda_wrapped_event_compact
|
||||
else
|
||||
R.layout.agenda_wrapped_event
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchange
|
||||
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import java.util.*
|
||||
|
||||
class LessonChangeCounter(
|
||||
val lessonChangeDate: Date,
|
||||
var lessonChangeCount: Int
|
||||
) {
|
||||
val startTime: Calendar
|
||||
get() = Calendar.getInstance().apply {
|
||||
set(lessonChangeDate.year, lessonChangeDate.month - 1, lessonChangeDate.day, 10, 0, 0)
|
||||
}
|
||||
|
||||
val endTime: Calendar
|
||||
get() = Calendar.getInstance().apply {
|
||||
timeInMillis = startTime.timeInMillis + (45 * 60 * 1000)
|
||||
}
|
||||
}
|
@ -1,243 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchange;
|
||||
|
||||
import com.github.tibolte.agendacalendarview.models.CalendarEvent;
|
||||
import com.github.tibolte.agendacalendarview.models.IDayItem;
|
||||
import com.github.tibolte.agendacalendarview.models.IWeekItem;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
|
||||
public class LessonChangeEvent implements CalendarEvent {
|
||||
|
||||
/**
|
||||
* Id of the event.
|
||||
*/
|
||||
private long mId;
|
||||
/**
|
||||
* Color to be displayed in the agenda view.
|
||||
*/
|
||||
private int mColor;
|
||||
/**
|
||||
* Text color displayed on the background color
|
||||
*/
|
||||
private int mTextColor;
|
||||
/**
|
||||
* Calendar instance helping sorting the events per section in the agenda view.
|
||||
*/
|
||||
private Calendar mInstanceDay;
|
||||
/**
|
||||
* Start time of the event.
|
||||
*/
|
||||
private Calendar mStartTime;
|
||||
/**
|
||||
* End time of the event.
|
||||
*/
|
||||
private Calendar mEndTime;
|
||||
/**
|
||||
* References to a DayItem instance for that event, used to link interaction between the
|
||||
* calendar view and the agenda view.
|
||||
*/
|
||||
private IDayItem mDayReference;
|
||||
/**
|
||||
* References to a WeekItem instance for that event, used to link interaction between the
|
||||
* calendar view and the agenda view.
|
||||
*/
|
||||
private IWeekItem mWeekReference;
|
||||
|
||||
|
||||
private int profileId;
|
||||
private Date lessonChangeDate;
|
||||
private int lessonChangeCount;
|
||||
|
||||
public LessonChangeEvent(LessonChangeEvent calendarEvent) {
|
||||
this.mId = calendarEvent.getId();
|
||||
this.mColor = calendarEvent.getColor();
|
||||
this.mTextColor = calendarEvent.getTextColor();
|
||||
this.mStartTime = calendarEvent.getStartTime();
|
||||
this.mEndTime = calendarEvent.getEndTime();
|
||||
this.profileId = calendarEvent.getProfileId();
|
||||
this.lessonChangeDate = calendarEvent.getLessonChangeDate();
|
||||
this.lessonChangeCount = calendarEvent.getLessonChangeCount();
|
||||
}
|
||||
|
||||
public LessonChangeEvent(long mId, int mColor, int mTextColor, Calendar mStartTime, Calendar mEndTime, int profileId, Date lessonChangeDate, int lessonChangeCount) {
|
||||
this.mId = mId;
|
||||
this.mColor = mColor;
|
||||
this.mTextColor = mTextColor;
|
||||
this.mStartTime = mStartTime;
|
||||
this.mEndTime = mEndTime;
|
||||
this.profileId = profileId;
|
||||
this.lessonChangeDate = lessonChangeDate;
|
||||
this.lessonChangeCount = lessonChangeCount;
|
||||
}
|
||||
|
||||
public int getProfileId() {
|
||||
return profileId;
|
||||
}
|
||||
|
||||
public Date getLessonChangeDate() {
|
||||
return lessonChangeDate;
|
||||
}
|
||||
|
||||
public int getLessonChangeCount() {
|
||||
return lessonChangeCount;
|
||||
}
|
||||
|
||||
public void setProfileId(int profileId) {
|
||||
this.profileId = profileId;
|
||||
}
|
||||
|
||||
public void setLessonChangeDate(Date lessonChangeDate) {
|
||||
this.lessonChangeDate = lessonChangeDate;
|
||||
}
|
||||
|
||||
public void setLessonChangeCount(int lessonChangeCount) {
|
||||
this.lessonChangeCount = lessonChangeCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlaceholder(boolean placeholder) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlaceholder() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocation(String mLocation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setId(long mId) {
|
||||
this.mId = mId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getShowBadge() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShowBadge(boolean mShowBadge) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTextColor() {
|
||||
return mTextColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTextColor(int mTextColor) {
|
||||
this.mTextColor = mTextColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDescription(String mDescription) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllDay() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAllDay(boolean allDay) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Calendar getStartTime() {
|
||||
return mStartTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStartTime(Calendar mStartTime) {
|
||||
this.mStartTime = mStartTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Calendar getEndTime() {
|
||||
return mEndTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEndTime(Calendar mEndTime) {
|
||||
this.mEndTime = mEndTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(String mTitle) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Calendar getInstanceDay() {
|
||||
return mInstanceDay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInstanceDay(Calendar mInstanceDay) {
|
||||
this.mInstanceDay = mInstanceDay;
|
||||
this.mInstanceDay.set(Calendar.HOUR, 0);
|
||||
this.mInstanceDay.set(Calendar.MINUTE, 0);
|
||||
this.mInstanceDay.set(Calendar.SECOND, 0);
|
||||
this.mInstanceDay.set(Calendar.MILLISECOND, 0);
|
||||
this.mInstanceDay.set(Calendar.AM_PM, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IDayItem getDayReference() {
|
||||
return mDayReference;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDayReference(IDayItem mDayReference) {
|
||||
this.mDayReference = mDayReference;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWeekItem getWeekReference() {
|
||||
return mWeekReference;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWeekReference(IWeekItem mWeekReference) {
|
||||
this.mWeekReference = mWeekReference;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CalendarEvent copy() {
|
||||
return new LessonChangeEvent(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColor() {
|
||||
return mColor;
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchange
|
||||
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.cardview.widget.CardView
|
||||
import com.github.tibolte.agendacalendarview.render.EventRenderer
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
|
||||
class LessonChangeEventRenderer : EventRenderer<LessonChangeEvent>() {
|
||||
override fun render(view: View?, event: LessonChangeEvent) {
|
||||
val card = view?.findViewById<CardView>(R.id.lesson_change_card)
|
||||
val changeText = view?.findViewById<TextView>(R.id.lesson_change_text)
|
||||
val changeCount = view?.findViewById<TextView>(R.id.lessonChangeCount)
|
||||
card?.setCardBackgroundColor(event.color)
|
||||
changeText?.setTextColor(event.textColor)
|
||||
changeCount?.setTextColor(event.textColor)
|
||||
changeCount?.text = event.lessonChangeCount.toString()
|
||||
}
|
||||
|
||||
override fun getEventLayout(): Int = R.layout.agenda_event_lesson_change
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-4-8.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchanges
|
||||
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.BaseEvent
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class LessonChangesEvent(
|
||||
val profileId: Int,
|
||||
val date: Date,
|
||||
val count: Int,
|
||||
showBadge: Boolean
|
||||
) : BaseEvent(
|
||||
id = date.value.toLong(),
|
||||
time = date.asCalendar,
|
||||
color = 0xff78909c.toInt(),
|
||||
showBadge = false,
|
||||
showItemBadge = showBadge
|
||||
) {
|
||||
override fun copy() = LessonChangesEvent(profileId, date, count, showItemBadge)
|
||||
|
||||
override fun getShowBadge() = false
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-4-8.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchanges
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import com.github.tibolte.agendacalendarview.render.EventRenderer
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.databinding.AgendaCounterItemBinding
|
||||
import pl.szczodrzynski.edziennik.databinding.AgendaWrappedCounterBinding
|
||||
import pl.szczodrzynski.edziennik.resolveAttr
|
||||
import pl.szczodrzynski.edziennik.setTintColor
|
||||
import pl.szczodrzynski.edziennik.utils.Colors
|
||||
|
||||
class LessonChangesEventRenderer : EventRenderer<LessonChangesEvent>() {
|
||||
|
||||
override fun render(view: View, event: LessonChangesEvent) {
|
||||
val b = AgendaWrappedCounterBinding.bind(view).item
|
||||
val textColor = Colors.legibleTextColor(event.color)
|
||||
|
||||
b.card.foreground.setTintColor(event.color)
|
||||
b.card.background.setTintColor(event.color)
|
||||
b.name.setText(R.string.agenda_lesson_changes)
|
||||
b.name.setTextColor(textColor)
|
||||
b.count.text = event.count.toString()
|
||||
b.count.setTextColor(textColor)
|
||||
|
||||
b.badgeBackground.isVisible = event.showItemBadge
|
||||
b.badgeBackground.background.setTintColor(
|
||||
android.R.attr.colorBackground.resolveAttr(view.context)
|
||||
)
|
||||
b.badge.isVisible = event.showItemBadge
|
||||
}
|
||||
|
||||
fun render(b: AgendaCounterItemBinding, event: LessonChangesEvent) {
|
||||
val textColor = Colors.legibleTextColor(event.color)
|
||||
|
||||
b.card.foreground.setTintColor(event.color)
|
||||
b.card.background.setTintColor(event.color)
|
||||
b.name.setText(R.string.agenda_lesson_changes)
|
||||
b.name.setTextColor(textColor)
|
||||
b.count.text = event.count.toString()
|
||||
b.count.setTextColor(textColor)
|
||||
|
||||
b.badgeBackground.isVisible = event.showItemBadge
|
||||
b.badgeBackground.background.setTintColor(
|
||||
android.R.attr.colorBackground.resolveAttr(b.root.context)
|
||||
)
|
||||
b.badge.isVisible = event.showItemBadge
|
||||
}
|
||||
|
||||
override fun getEventLayout(): Int = R.layout.agenda_wrapped_counter
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence
|
||||
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import java.util.*
|
||||
|
||||
class TeacherAbsenceCounter (
|
||||
val teacherAbsenceDate: Date,
|
||||
var teacherAbsenceCount: Int = 0
|
||||
) {
|
||||
val startTime: Calendar
|
||||
get() = Calendar.getInstance().apply {
|
||||
set(teacherAbsenceDate.year, teacherAbsenceDate.month - 1, teacherAbsenceDate.day, 10, 0, 0)
|
||||
}
|
||||
|
||||
val endTime: Calendar
|
||||
get() = Calendar.getInstance().apply {
|
||||
timeInMillis = startTime.timeInMillis + (45 * 60 * 1000)
|
||||
}
|
||||
}
|
@ -1,188 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-4-8.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence
|
||||
|
||||
import com.github.tibolte.agendacalendarview.models.CalendarEvent
|
||||
import com.github.tibolte.agendacalendarview.models.IDayItem
|
||||
import com.github.tibolte.agendacalendarview.models.IWeekItem
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.BaseEvent
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import java.util.*
|
||||
|
||||
class TeacherAbsenceEvent : CalendarEvent {
|
||||
/**
|
||||
* Id of the event.
|
||||
*/
|
||||
private var mId: Long = 0
|
||||
/**
|
||||
* Color to be displayed in the agenda view.
|
||||
*/
|
||||
private var mColor: Int = 0
|
||||
/**
|
||||
* Text color displayed on the background color
|
||||
*/
|
||||
private var mTextColor: Int = 0
|
||||
/**
|
||||
* Calendar instance helping sorting the events per section in the agenda view.
|
||||
*/
|
||||
private var mInstanceDay: Calendar? = null
|
||||
/**
|
||||
* Start time of the event.
|
||||
*/
|
||||
private var mStartTime: Calendar? = null
|
||||
/**
|
||||
* End time of the event.
|
||||
*/
|
||||
private var mEndTime: Calendar? = null
|
||||
/**
|
||||
* References to a DayItem instance for that event, used to link interaction between the
|
||||
* calendar view and the agenda view.
|
||||
*/
|
||||
private var mDayReference: IDayItem? = null
|
||||
/**
|
||||
* References to a WeekItem instance for that event, used to link interaction between the
|
||||
* calendar view and the agenda view.
|
||||
*/
|
||||
private var mWeekReference: IWeekItem? = null
|
||||
|
||||
|
||||
private var profileId: Int = 0
|
||||
var teacherAbsenceDate: Date? = null
|
||||
var teacherAbsenceCount: Int = 0
|
||||
|
||||
constructor(calendarEvent: TeacherAbsenceEvent) {
|
||||
this.mId = calendarEvent.id
|
||||
this.mColor = calendarEvent.color
|
||||
this.mTextColor = calendarEvent.textColor
|
||||
this.mStartTime = calendarEvent.startTime
|
||||
this.mEndTime = calendarEvent.endTime
|
||||
this.profileId = calendarEvent.profileId
|
||||
this.teacherAbsenceDate = calendarEvent.teacherAbsenceDate
|
||||
this.teacherAbsenceCount = calendarEvent.teacherAbsenceCount
|
||||
}
|
||||
|
||||
constructor(mId: Long, mColor: Int, mTextColor: Int, mStartTime: Calendar, mEndTime: Calendar, profileId: Int, teacherAbsenceDate: Date, teacherAbsenceCount: Int) {
|
||||
this.mId = mId
|
||||
this.mColor = mColor
|
||||
this.mTextColor = mTextColor
|
||||
this.mStartTime = mStartTime
|
||||
this.mEndTime = mEndTime
|
||||
this.profileId = profileId
|
||||
this.teacherAbsenceDate = teacherAbsenceDate
|
||||
this.teacherAbsenceCount = teacherAbsenceCount
|
||||
}
|
||||
|
||||
override fun setPlaceholder(placeholder: Boolean) {
|
||||
|
||||
}
|
||||
|
||||
override fun isPlaceholder(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getLocation(): String? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun setLocation(mLocation: String) {
|
||||
|
||||
}
|
||||
|
||||
override fun getId(): Long {
|
||||
return mId
|
||||
}
|
||||
|
||||
override fun setId(mId: Long) {
|
||||
this.mId = mId
|
||||
}
|
||||
|
||||
override fun getShowBadge(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setShowBadge(mShowBadge: Boolean) {
|
||||
|
||||
}
|
||||
|
||||
override fun getTextColor(): Int {
|
||||
return mTextColor
|
||||
}
|
||||
|
||||
override fun setTextColor(mTextColor: Int) {
|
||||
this.mTextColor = mTextColor
|
||||
}
|
||||
|
||||
override fun getDescription(): String? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun setDescription(mDescription: String) {
|
||||
|
||||
}
|
||||
|
||||
override fun isAllDay(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setAllDay(allDay: Boolean) {
|
||||
|
||||
}
|
||||
|
||||
override fun getStartTime(): Calendar? {
|
||||
return mStartTime
|
||||
}
|
||||
|
||||
override fun setStartTime(mStartTime: Calendar) {
|
||||
this.mStartTime = mStartTime
|
||||
}
|
||||
|
||||
override fun getEndTime(): Calendar? {
|
||||
return mEndTime
|
||||
}
|
||||
|
||||
override fun setEndTime(mEndTime: Calendar) {
|
||||
this.mEndTime = mEndTime
|
||||
}
|
||||
|
||||
override fun getTitle(): String? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun setTitle(mTitle: String) {
|
||||
|
||||
}
|
||||
|
||||
override fun getInstanceDay(): Calendar? {
|
||||
return mInstanceDay
|
||||
}
|
||||
|
||||
override fun setInstanceDay(mInstanceDay: Calendar) {
|
||||
this.mInstanceDay = mInstanceDay
|
||||
this.mInstanceDay!!.set(Calendar.HOUR, 0)
|
||||
this.mInstanceDay!!.set(Calendar.MINUTE, 0)
|
||||
this.mInstanceDay!!.set(Calendar.SECOND, 0)
|
||||
this.mInstanceDay!!.set(Calendar.MILLISECOND, 0)
|
||||
this.mInstanceDay!!.set(Calendar.AM_PM, 0)
|
||||
}
|
||||
|
||||
override fun getDayReference(): IDayItem? {
|
||||
return mDayReference
|
||||
}
|
||||
|
||||
override fun setDayReference(mDayReference: IDayItem) {
|
||||
this.mDayReference = mDayReference
|
||||
}
|
||||
|
||||
override fun getWeekReference(): IWeekItem? {
|
||||
return mWeekReference
|
||||
}
|
||||
|
||||
override fun setWeekReference(mWeekReference: IWeekItem) {
|
||||
this.mWeekReference = mWeekReference
|
||||
}
|
||||
|
||||
override fun copy(): CalendarEvent {
|
||||
return TeacherAbsenceEvent(this)
|
||||
}
|
||||
|
||||
override fun getColor(): Int {
|
||||
return mColor
|
||||
}
|
||||
class TeacherAbsenceEvent(
|
||||
val profileId: Int,
|
||||
val date: Date,
|
||||
val count: Int
|
||||
) : BaseEvent(
|
||||
id = date.value.toLong(),
|
||||
time = date.asCalendar,
|
||||
color = 0xffff1744.toInt(),
|
||||
showBadge = false
|
||||
) {
|
||||
override fun copy() = TeacherAbsenceEvent(profileId, date, count)
|
||||
}
|
||||
|
@ -1,21 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-4-8.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence
|
||||
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.cardview.widget.CardView
|
||||
import androidx.core.view.isVisible
|
||||
import com.github.tibolte.agendacalendarview.render.EventRenderer
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.databinding.AgendaCounterItemBinding
|
||||
import pl.szczodrzynski.edziennik.databinding.AgendaWrappedCounterBinding
|
||||
import pl.szczodrzynski.edziennik.setTintColor
|
||||
import pl.szczodrzynski.edziennik.utils.Colors
|
||||
|
||||
class TeacherAbsenceEventRenderer : EventRenderer<TeacherAbsenceEvent>() {
|
||||
override fun render(view: View?, event: TeacherAbsenceEvent) {
|
||||
val card = view?.findViewById<CardView>(R.id.teacherAbsenceCard)
|
||||
val changeText = view?.findViewById<TextView>(R.id.teacherAbsenceText)
|
||||
val changeCount = view?.findViewById<TextView>(R.id.teacherAbsenceCount)
|
||||
card?.setCardBackgroundColor(event.color)
|
||||
changeText?.setTextColor(event.textColor)
|
||||
changeCount?.setTextColor(event.textColor)
|
||||
changeCount?.text = event.teacherAbsenceCount.toString()
|
||||
|
||||
override fun render(view: View, event: TeacherAbsenceEvent) {
|
||||
val b = AgendaWrappedCounterBinding.bind(view).item
|
||||
val textColor = Colors.legibleTextColor(event.color)
|
||||
|
||||
b.card.foreground.setTintColor(event.color)
|
||||
b.card.background.setTintColor(event.color)
|
||||
b.name.setText(R.string.agenda_teacher_absence)
|
||||
b.name.setTextColor(textColor)
|
||||
b.count.text = event.count.toString()
|
||||
b.count.setTextColor(textColor)
|
||||
|
||||
b.badgeBackground.isVisible = false
|
||||
b.badge.isVisible = false
|
||||
}
|
||||
|
||||
override fun getEventLayout(): Int = R.layout.agenda_event_teacher_absence
|
||||
fun render(b: AgendaCounterItemBinding, event: TeacherAbsenceEvent) {
|
||||
val textColor = Colors.legibleTextColor(event.color)
|
||||
|
||||
b.card.foreground.setTintColor(event.color)
|
||||
b.card.background.setTintColor(event.color)
|
||||
b.name.setText(R.string.agenda_teacher_absence)
|
||||
b.name.setTextColor(textColor)
|
||||
b.count.text = event.count.toString()
|
||||
b.count.setTextColor(textColor)
|
||||
|
||||
b.badgeBackground.isVisible = false
|
||||
b.badge.isVisible = false
|
||||
}
|
||||
|
||||
override fun getEventLayout(): Int = R.layout.agenda_wrapped_counter
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
|
||||
import pl.szczodrzynski.edziennik.databinding.AttendanceDetailsDialogBinding
|
||||
import pl.szczodrzynski.edziennik.setTintColor
|
||||
import pl.szczodrzynski.edziennik.utils.BetterLink
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class AttendanceDetailsDialog(
|
||||
@ -60,5 +61,13 @@ class AttendanceDetailsDialog(
|
||||
b.attendanceName.background.setTintColor(attendanceColor)
|
||||
|
||||
b.attendanceIsCounted.setText(if (attendance.isCounted) R.string.yes else R.string.no)
|
||||
|
||||
attendance.teacherName?.let { name ->
|
||||
BetterLink.attach(
|
||||
b.teacherName,
|
||||
teachers = mapOf(attendance.teacherId to name),
|
||||
onActionSelected = dialog::dismiss
|
||||
)
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
@ -41,7 +41,8 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
// local/private variables go here
|
||||
private val manager by lazy { app.attendanceManager }
|
||||
private val manager
|
||||
get() = app.attendanceManager
|
||||
private var viewType = AttendanceFragment.VIEW_DAYS
|
||||
private var expandSubjectId = 0L
|
||||
|
||||
|
@ -47,7 +47,8 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
// local/private variables go here
|
||||
private val manager by lazy { app.attendanceManager }
|
||||
private val manager
|
||||
get() = app.attendanceManager
|
||||
private var expandSubjectId = 0L
|
||||
private var attendance = listOf<AttendanceFull>()
|
||||
|
||||
|
@ -21,6 +21,7 @@ import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_MOBIDZIENNIK
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notice
|
||||
import pl.szczodrzynski.edziennik.data.db.full.NoticeFull
|
||||
import pl.szczodrzynski.edziennik.utils.BetterLink
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.bs
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
@ -83,6 +84,14 @@ class NoticesAdapter//getting the context and product list with constructor
|
||||
} else {
|
||||
holder.noticesItemReason.background = null
|
||||
}
|
||||
|
||||
BetterLink.attach(holder.noticesItemReason)
|
||||
|
||||
notice.teacherName?.let { name ->
|
||||
BetterLink.attach(holder.noticesItemTeacherName, teachers = mapOf(
|
||||
notice.teacherId to name
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
|
@ -40,7 +40,8 @@ class GradesAdapter(
|
||||
}
|
||||
|
||||
private val app = activity.applicationContext as App
|
||||
private val manager = app.gradesManager
|
||||
private val manager
|
||||
get() = app.gradesManager
|
||||
|
||||
private val job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
|
@ -48,9 +48,12 @@ class GradesListFragment : Fragment(), CoroutineScope {
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
// local/private variables go here
|
||||
private val manager by lazy { app.gradesManager }
|
||||
private val dontCountEnabled by lazy { manager.dontCountEnabled }
|
||||
private val dontCountGrades by lazy { manager.dontCountGrades }
|
||||
private val manager
|
||||
get() = app.gradesManager
|
||||
private val dontCountEnabled
|
||||
get() = manager.dontCountEnabled
|
||||
private val dontCountGrades
|
||||
get() = manager.dontCountGrades
|
||||
private var expandSubjectId = 0L
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
|
@ -95,9 +95,11 @@ class LoginEggsFragment : Fragment(), CoroutineScope {
|
||||
anim.interpolator = AccelerateDecelerateInterpolator()
|
||||
anim.duration = 10
|
||||
anim.fillAfter = true
|
||||
activity.runOnUiThread {
|
||||
activity.getRootView().startAnimation(anim)
|
||||
nav.navigate(R.id.loginPrizeFragment, null, activity.navOptions)
|
||||
}
|
||||
}
|
||||
}, "EggInterface")
|
||||
loadUrl("https://szkolny.eu/game/runner.html")
|
||||
webViewClient = object : WebViewClient() {
|
||||
|
@ -30,10 +30,12 @@ import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_SENT
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
import pl.szczodrzynski.edziennik.databinding.MessageFragmentBinding
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.MessagesConfigDialog
|
||||
import pl.szczodrzynski.edziennik.utils.Anim
|
||||
import pl.szczodrzynski.edziennik.utils.BetterLink
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
||||
import pl.szczodrzynski.navlib.colorAttr
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.math.min
|
||||
@ -64,10 +66,20 @@ class MessageFragment : Fragment(), CoroutineScope {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
if (!isAdded) return
|
||||
|
||||
activity.bottomSheet.prependItem(
|
||||
BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_messages_config)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_cog_outline)
|
||||
.withOnClickListener {
|
||||
activity.bottomSheet.close()
|
||||
MessagesConfigDialog(activity, false, null, null)
|
||||
}
|
||||
)
|
||||
|
||||
b.closeButton.setImageDrawable(
|
||||
IconicsDrawable(activity, CommunityMaterial.Icon3.cmd_window_close).apply {
|
||||
colorAttr(activity, android.R.attr.textColorSecondary)
|
||||
sizeDp = 16
|
||||
sizeDp = 24
|
||||
}
|
||||
)
|
||||
b.closeButton.setOnClickListener { activity.navigateUp() }
|
||||
|
@ -1,11 +1,8 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.messages
|
||||
|
||||
import android.graphics.Typeface
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Filter
|
||||
import android.widget.Filterable
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@ -13,17 +10,14 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.cleanDiacritics
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.models.MessagesSearch
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.utils.MessagesFilter
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.viewholder.MessageViewHolder
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.viewholder.SearchViewHolder
|
||||
import java.util.*
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.math.min
|
||||
|
||||
class MessagesAdapter(
|
||||
val activity: AppCompatActivity,
|
||||
@ -43,41 +37,10 @@ class MessagesAdapter(
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
var items = mutableListOf<Any>()
|
||||
var allItems = mutableListOf<Any>()
|
||||
var items = listOf<Any>()
|
||||
var allItems = listOf<Any>()
|
||||
val typefaceNormal: Typeface by lazy { Typeface.create(Typeface.DEFAULT, Typeface.NORMAL) }
|
||||
val typefaceBold: Typeface by lazy { Typeface.create(Typeface.DEFAULT, Typeface.BOLD) }
|
||||
private val comparator by lazy { Comparator { o1: Any, o2: Any ->
|
||||
if (o1 !is MessageFull || o2 !is MessageFull)
|
||||
return@Comparator 0
|
||||
when {
|
||||
// standard sorting
|
||||
o1.filterWeight > o2.filterWeight -> return@Comparator 1
|
||||
o1.filterWeight < o2.filterWeight -> return@Comparator -1
|
||||
else -> when {
|
||||
// reversed sorting
|
||||
o1.addedDate > o2.addedDate -> return@Comparator -1
|
||||
o1.addedDate < o2.addedDate -> return@Comparator 1
|
||||
else -> return@Comparator 0
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
val textWatcher by lazy {
|
||||
object : TextWatcher {
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
getFilter().filter(s.toString())
|
||||
}
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||
/*items.getOrNull(0)?.let {
|
||||
if (it is MessagesSearch) {
|
||||
it.searchText = s?.toString() ?: ""
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
@ -103,138 +66,16 @@ class MessagesAdapter(
|
||||
return
|
||||
|
||||
when {
|
||||
holder is MessageViewHolder && item is MessageFull -> holder.onBind(activity, app, item, position, this)
|
||||
holder is SearchViewHolder && item is MessagesSearch -> holder.onBind(activity, app, item, position, this)
|
||||
holder is MessageViewHolder
|
||||
&& item is MessageFull -> holder.onBind(activity, app, item, position, this)
|
||||
holder is SearchViewHolder
|
||||
&& item is MessagesSearch -> holder.onBind(activity, app, item, position, this)
|
||||
}
|
||||
}
|
||||
|
||||
private val messagesFilter by lazy {
|
||||
MessagesFilter(this)
|
||||
}
|
||||
override fun getItemCount() = items.size
|
||||
override fun getFilter() = filter
|
||||
private var prevCount = -1
|
||||
private val filter by lazy { object : Filter() {
|
||||
override fun performFiltering(prefix: CharSequence?): FilterResults {
|
||||
val results = FilterResults()
|
||||
|
||||
if (prevCount == -1)
|
||||
prevCount = allItems.size
|
||||
|
||||
if (prefix.isNullOrEmpty()) {
|
||||
allItems.forEach {
|
||||
if (it is MessageFull)
|
||||
it.searchHighlightText = null
|
||||
}
|
||||
results.values = allItems.toList()
|
||||
results.count = allItems.size
|
||||
return results
|
||||
}
|
||||
|
||||
val items = mutableListOf<Any>()
|
||||
val prefixString = prefix.toString()
|
||||
|
||||
allItems.forEach {
|
||||
if (it !is MessageFull) {
|
||||
items.add(it)
|
||||
return@forEach
|
||||
}
|
||||
it.filterWeight = 100
|
||||
it.searchHighlightText = null
|
||||
|
||||
var weight: Int
|
||||
if (it.type == Message.TYPE_SENT) {
|
||||
it.recipients?.forEach { recipient ->
|
||||
weight = getMatchWeight(recipient.fullName, prefixString)
|
||||
if (weight != 100) {
|
||||
if (weight == 3)
|
||||
weight = 31
|
||||
it.filterWeight = min(it.filterWeight, 10 + weight)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
weight = getMatchWeight(it.senderName, prefixString)
|
||||
if (weight != 100) {
|
||||
if (weight == 3)
|
||||
weight = 31
|
||||
it.filterWeight = min(it.filterWeight, 10 + weight)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
weight = getMatchWeight(it.subject, prefixString)
|
||||
if (weight != 100) {
|
||||
if (weight == 3)
|
||||
weight = 22
|
||||
it.filterWeight = min(it.filterWeight, 20 + weight)
|
||||
}
|
||||
|
||||
if (it.filterWeight != 100) {
|
||||
it.searchHighlightText = prefixString
|
||||
items.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(items, comparator)
|
||||
results.values = items
|
||||
results.count = items.size
|
||||
return results
|
||||
}
|
||||
|
||||
override fun publishResults(constraint: CharSequence?, results: FilterResults) {
|
||||
results.values?.let { items = it as MutableList<Any> }
|
||||
// do not re-bind the search box
|
||||
val count = results.count - 1
|
||||
|
||||
// this tries to update every item except the search field
|
||||
when {
|
||||
count > prevCount -> {
|
||||
notifyItemRangeInserted(prevCount + 1, count - prevCount)
|
||||
notifyItemRangeChanged(1, prevCount)
|
||||
}
|
||||
count < prevCount -> {
|
||||
notifyItemRangeRemoved(prevCount + 1, prevCount - count)
|
||||
notifyItemRangeChanged(1, count)
|
||||
}
|
||||
else -> {
|
||||
notifyItemRangeChanged(1, count)
|
||||
}
|
||||
}
|
||||
|
||||
/*if (prevCount != count) {
|
||||
items.getOrNull(0)?.let {
|
||||
if (it is MessagesSearch) {
|
||||
it.count = count
|
||||
notifyItemChanged(0)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
prevCount = count
|
||||
}
|
||||
}}
|
||||
|
||||
private fun getMatchWeight(name: CharSequence?, prefix: String): Int {
|
||||
if (name == null)
|
||||
return 100
|
||||
|
||||
val nameClean = name.cleanDiacritics()
|
||||
|
||||
// First match against the whole, non-split value
|
||||
if (nameClean.startsWith(prefix, ignoreCase = true) || name.startsWith(prefix, ignoreCase = true)) {
|
||||
return 1
|
||||
} else {
|
||||
// check if prefix matches any of the words
|
||||
val words = nameClean.split(" ").toTypedArray() + name.split(" ").toTypedArray()
|
||||
for (word in words) {
|
||||
if (word.startsWith(prefix, ignoreCase = true)) {
|
||||
return 2
|
||||
}
|
||||
}
|
||||
}
|
||||
// finally check if the prefix matches any part of the name
|
||||
if (nameClean.contains(prefix, ignoreCase = true) || name.contains(prefix, ignoreCase = true)) {
|
||||
return 3
|
||||
}
|
||||
|
||||
return 100
|
||||
}
|
||||
override fun getFilter() = messagesFilter
|
||||
}
|
||||
|
@ -12,7 +12,9 @@ import kotlinx.coroutines.Job
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||
import pl.szczodrzynski.edziennik.databinding.MessagesFragmentBinding
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.MessagesConfigDialog
|
||||
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.FragmentLazyPagerAdapter
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class MessagesFragment : Fragment(), CoroutineScope {
|
||||
@ -100,9 +102,19 @@ class MessagesFragment : Fragment(), CoroutineScope {
|
||||
fabIcon = CommunityMaterial.Icon3.cmd_pencil_outline
|
||||
}
|
||||
|
||||
setFabOnClickListener(View.OnClickListener {
|
||||
bottomSheet.prependItem(
|
||||
BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_messages_config)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_cog_outline)
|
||||
.withOnClickListener {
|
||||
activity.bottomSheet.close()
|
||||
MessagesConfigDialog(activity, false, null, null)
|
||||
}
|
||||
)
|
||||
|
||||
setFabOnClickListener {
|
||||
activity.loadTarget(MainActivity.TARGET_MESSAGES_COMPOSE)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
activity.gainAttentionFAB()
|
||||
|
@ -33,6 +33,7 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
|
||||
private lateinit var app: App
|
||||
private lateinit var activity: MainActivity
|
||||
private lateinit var b: MessagesListFragmentBinding
|
||||
private var adapter: MessagesAdapter? = null
|
||||
|
||||
private val job: Job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
@ -53,21 +54,22 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
|
||||
val messageType = arguments.getInt("messageType", Message.TYPE_RECEIVED)
|
||||
var topPosition = arguments.getInt("topPosition", NO_POSITION)
|
||||
var bottomPosition = arguments.getInt("bottomPosition", NO_POSITION)
|
||||
val searchText = arguments.getString("searchText", "")
|
||||
|
||||
teachers = withContext(Dispatchers.Default) {
|
||||
app.db.teacherDao().getAllNow(App.profileId)
|
||||
}
|
||||
|
||||
val adapter = MessagesAdapter(activity, teachers) {
|
||||
adapter = MessagesAdapter(activity, teachers) {
|
||||
activity.loadTarget(MainActivity.TARGET_MESSAGES_DETAILS, Bundle(
|
||||
"messageId" to it.id
|
||||
))
|
||||
}
|
||||
|
||||
app.db.messageDao().getAllByType(App.profileId, messageType).observe(this@MessagesListFragment, Observer { items ->
|
||||
app.db.messageDao().getAllByType(App.profileId, messageType).observe(this@MessagesListFragment, Observer { messages ->
|
||||
if (!isAdded) return@Observer
|
||||
|
||||
items.forEach { message ->
|
||||
messages.forEach { message ->
|
||||
message.recipients?.removeAll { it.profileId != message.profileId }
|
||||
message.recipients?.forEach { recipient ->
|
||||
if (recipient.fullName == null) {
|
||||
@ -77,13 +79,22 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
|
||||
}
|
||||
|
||||
// load & configure the adapter
|
||||
adapter.items = items.toMutableList()
|
||||
adapter.items.add(0, MessagesSearch().also {
|
||||
it.count = items.size
|
||||
val items = messages.toMutableList<Any>()
|
||||
items.add(0, MessagesSearch().also {
|
||||
it.searchText = searchText
|
||||
})
|
||||
adapter.allItems = adapter.items.toMutableList()
|
||||
|
||||
adapter?.items = items
|
||||
adapter?.allItems = items
|
||||
|
||||
if (items.isNotNullNorEmpty() && b.list.adapter == null) {
|
||||
if (searchText.isNotBlank())
|
||||
adapter?.filter?.filter(searchText) {
|
||||
b.list.adapter = adapter
|
||||
}
|
||||
else
|
||||
b.list.adapter = adapter
|
||||
|
||||
b.list.apply {
|
||||
setHasFixedSize(true)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
@ -92,7 +103,7 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
|
||||
addOnScrollListener(onScrollListener)
|
||||
}
|
||||
}
|
||||
adapter.notifyDataSetChanged()
|
||||
|
||||
setSwipeToRefresh(messageType in Message.TYPE_RECEIVED..Message.TYPE_SENT && items.isNullOrEmpty())
|
||||
|
||||
(b.list.layoutManager as? LinearLayoutManager)?.let { layoutManager ->
|
||||
@ -119,10 +130,15 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
if (!isAdded) return
|
||||
if (!isAdded)
|
||||
return
|
||||
val layoutManager = (b.list.layoutManager as? LinearLayoutManager)
|
||||
val searchItem = adapter?.items?.firstOrNull { it is MessagesSearch } as? MessagesSearch
|
||||
|
||||
onPageDestroy?.invoke(position, Bundle(
|
||||
"topPosition" to (b.list.layoutManager as? LinearLayoutManager)?.findFirstVisibleItemPosition(),
|
||||
"bottomPosition" to (b.list.layoutManager as? LinearLayoutManager)?.findLastCompletelyVisibleItemPosition()
|
||||
"topPosition" to layoutManager?.findFirstVisibleItemPosition(),
|
||||
"bottomPosition" to layoutManager?.findLastCompletelyVisibleItemPosition(),
|
||||
"searchText" to searchItem?.searchText?.toString()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.messages.compose
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
@ -43,6 +44,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
import pl.szczodrzynski.edziennik.databinding.MessagesComposeFragmentBinding
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.MessagesConfigDialog
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesUtils
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesUtils.getProfileImage
|
||||
import pl.szczodrzynski.edziennik.utils.Colors
|
||||
@ -50,6 +52,7 @@ import pl.szczodrzynski.edziennik.utils.Themes
|
||||
import pl.szczodrzynski.edziennik.utils.Themes.getPrimaryTextColor
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
||||
import pl.szczodrzynski.navlib.elevateSurface
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.text.replace
|
||||
@ -67,6 +70,10 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
private val profileConfig by lazy { app.config.forProfile().ui }
|
||||
private val greetingText
|
||||
get() = profileConfig.messagesGreetingText ?: "\n\nZ poważaniem\n${app.profile.accountOwnerName}"
|
||||
|
||||
private var teachers = mutableListOf<Teacher>()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
@ -131,6 +138,16 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
|
||||
}
|
||||
}*/
|
||||
|
||||
activity.bottomSheet.prependItem(
|
||||
BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_messages_config)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_cog_outline)
|
||||
.withOnClickListener {
|
||||
activity.bottomSheet.close()
|
||||
MessagesConfigDialog(activity, false, null, null)
|
||||
}
|
||||
)
|
||||
|
||||
launch {
|
||||
delay(100)
|
||||
getRecipientList()
|
||||
@ -290,7 +307,7 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
|
||||
b.recipients.setIllegalCharacterIdentifier { c ->
|
||||
c.toString().matches("[\\n;:_ ]".toRegex())
|
||||
}
|
||||
b.recipients.setOnChipRemoveListener { _ ->
|
||||
b.recipients.setOnChipRemoveListener {
|
||||
b.recipients.setSelection(b.recipients.text.length)
|
||||
}
|
||||
|
||||
@ -318,14 +335,15 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
|
||||
fabExtendedText = getString(R.string.messages_compose_send)
|
||||
fabIcon = CommunityMaterial.Icon3.cmd_send_outline
|
||||
|
||||
setFabOnClickListener(View.OnClickListener {
|
||||
setFabOnClickListener {
|
||||
sendMessage()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
activity.gainAttentionFAB()
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun updateRecipientList(list: List<Teacher>) { launch {
|
||||
withContext(Dispatchers.Default) {
|
||||
teachers = list.sortedBy { it.fullName }.toMutableList()
|
||||
@ -344,10 +362,14 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
|
||||
val adapter = MessagesComposeSuggestionAdapter(activity, teachers)
|
||||
b.recipients.setAdapter(adapter)
|
||||
|
||||
if (profileConfig.messagesGreetingOnCompose)
|
||||
b.text.setText(greetingText)
|
||||
|
||||
handleReplyMessage()
|
||||
handleMailToIntent()
|
||||
}}
|
||||
|
||||
private fun handleReplyMessage() { launch {
|
||||
private fun handleReplyMessage() = launch {
|
||||
val replyMessage = arguments?.getString("message")
|
||||
if (replyMessage != null) {
|
||||
val chipList = mutableListOf<ChipInfo>()
|
||||
@ -369,8 +391,10 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
|
||||
|
||||
if (arguments?.getString("type") == "reply") {
|
||||
// add greeting text
|
||||
span.replace(0, 0, "\n\nZ poważaniem,\n${app.profile.accountName
|
||||
?: app.profile.studentNameLong ?: ""}\n\n\n")
|
||||
if (profileConfig.messagesGreetingOnReply)
|
||||
span.replace(0, 0, "$greetingText\n\n\n")
|
||||
else
|
||||
span.replace(0, 0, "\n\n")
|
||||
|
||||
teachers.firstOrNull { it.id == msg.senderId }?.let { teacher ->
|
||||
teacher.image = getProfileImage(48, 24, 16, 12, 1, teacher.fullName)
|
||||
@ -378,7 +402,12 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
|
||||
}
|
||||
subject = "Re: ${msg.subject}"
|
||||
} else {
|
||||
// add greeting text
|
||||
if (profileConfig.messagesGreetingOnForward)
|
||||
span.replace(0, 0, "$greetingText\n\n\n")
|
||||
else
|
||||
span.replace(0, 0, "\n\n")
|
||||
|
||||
subject = "Fwd: ${msg.subject}"
|
||||
}
|
||||
body = MessagesUtils.htmlToSpannable(activity, msg.body
|
||||
@ -400,7 +429,23 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
|
||||
else {
|
||||
b.recipients.requestFocus()
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
private fun handleMailToIntent() {
|
||||
val teacherId = arguments?.getLong("messageRecipientId")
|
||||
if (teacherId == 0L)
|
||||
return
|
||||
|
||||
val chipList = mutableListOf<ChipInfo>()
|
||||
teachers.firstOrNull { it.id == teacherId }?.let { teacher ->
|
||||
teacher.image = getProfileImage(48, 24, 16, 12, 1, teacher.fullName)
|
||||
chipList += ChipInfo(teacher.fullName, teacher)
|
||||
}
|
||||
b.recipients.addTextWithChips(chipList)
|
||||
|
||||
val subject = arguments?.getString("messageSubject")
|
||||
b.subject.setText(subject ?: return)
|
||||
}
|
||||
|
||||
private fun sendMessage() {
|
||||
b.recipientsLayout.error = null
|
||||
|
@ -5,7 +5,5 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.messages.models
|
||||
|
||||
class MessagesSearch {
|
||||
var isFocused = false
|
||||
var searchText = ""
|
||||
var count = 0
|
||||
var searchText: CharSequence = ""
|
||||
}
|
||||
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-4-14.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.messages.utils
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
|
||||
class MessagesComparator : Comparator<Any> {
|
||||
|
||||
override fun compare(o1: Any?, o2: Any?): Int {
|
||||
if (o1 !is MessageFull || o2 !is MessageFull)
|
||||
return 0
|
||||
|
||||
return when {
|
||||
// standard sorting
|
||||
o1.filterWeight > o2.filterWeight -> 1
|
||||
o1.filterWeight < o2.filterWeight -> -1
|
||||
else -> when {
|
||||
// reversed sorting
|
||||
o1.addedDate > o2.addedDate -> -1
|
||||
o1.addedDate < o2.addedDate -> 1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-4-14.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.messages.utils
|
||||
|
||||
import android.widget.Filter
|
||||
import pl.szczodrzynski.edziennik.cleanDiacritics
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesAdapter
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
class MessagesFilter(
|
||||
private val adapter: MessagesAdapter
|
||||
) : Filter() {
|
||||
companion object {
|
||||
private const val NO_MATCH = 1000
|
||||
}
|
||||
|
||||
private val comparator = MessagesComparator()
|
||||
private var prevCount = -1
|
||||
|
||||
private val allItems
|
||||
get() = adapter.allItems
|
||||
|
||||
private fun getMatchWeight(name: CharSequence?, prefix: CharSequence): Int {
|
||||
if (name == null)
|
||||
return NO_MATCH
|
||||
|
||||
val prefixClean = prefix.cleanDiacritics()
|
||||
val nameClean = name.cleanDiacritics()
|
||||
|
||||
return when {
|
||||
// First match against the whole, non-split value
|
||||
nameClean.startsWith(prefixClean, ignoreCase = true) -> 1
|
||||
// check if prefix matches any of the words
|
||||
nameClean.split(" ").any {
|
||||
it.startsWith(prefixClean, ignoreCase = true)
|
||||
} -> 2
|
||||
// finally check if the prefix matches any part of the name
|
||||
nameClean.contains(prefixClean, ignoreCase = true) -> 3
|
||||
|
||||
else -> NO_MATCH
|
||||
}
|
||||
}
|
||||
|
||||
override fun performFiltering(prefix: CharSequence?): FilterResults {
|
||||
val results = FilterResults()
|
||||
|
||||
if (prevCount == -1)
|
||||
prevCount = allItems.size
|
||||
|
||||
if (prefix.isNullOrBlank()) {
|
||||
allItems.forEach {
|
||||
if (it is MessageFull)
|
||||
it.searchHighlightText = null
|
||||
}
|
||||
results.values = allItems.toList()
|
||||
results.count = allItems.size
|
||||
return results
|
||||
}
|
||||
|
||||
val items = mutableListOf<Any>()
|
||||
|
||||
allItems.forEach {
|
||||
if (it !is MessageFull) {
|
||||
items.add(it)
|
||||
return@forEach
|
||||
}
|
||||
it.filterWeight = NO_MATCH
|
||||
it.searchHighlightText = null
|
||||
|
||||
var weight: Int
|
||||
// weights 11..13 and 110
|
||||
if (it.type == Message.TYPE_SENT) {
|
||||
it.recipients?.forEach { recipient ->
|
||||
weight = getMatchWeight(recipient.fullName, prefix)
|
||||
if (weight != NO_MATCH) {
|
||||
if (weight == 3)
|
||||
weight = 100
|
||||
it.filterWeight = min(it.filterWeight, 10 + weight)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
weight = getMatchWeight(it.senderName, prefix)
|
||||
if (weight != NO_MATCH) {
|
||||
if (weight == 3)
|
||||
weight = 100
|
||||
it.filterWeight = min(it.filterWeight, 10 + weight)
|
||||
}
|
||||
}
|
||||
|
||||
// weights 21..23 and 120
|
||||
weight = getMatchWeight(it.subject, prefix)
|
||||
if (weight != NO_MATCH) {
|
||||
if (weight == 3)
|
||||
weight = 100
|
||||
it.filterWeight = min(it.filterWeight, 20 + weight)
|
||||
}
|
||||
|
||||
// weights 31..33 and 130
|
||||
weight = getMatchWeight(it.body, prefix)
|
||||
if (weight != NO_MATCH) {
|
||||
if (weight == 3)
|
||||
weight = 100
|
||||
it.filterWeight = min(it.filterWeight, 30 + weight)
|
||||
}
|
||||
|
||||
if (it.filterWeight != NO_MATCH) {
|
||||
it.searchHighlightText = prefix
|
||||
items.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(items, comparator)
|
||||
results.values = items
|
||||
results.count = items.size
|
||||
return results
|
||||
}
|
||||
|
||||
override fun publishResults(constraint: CharSequence?, results: FilterResults) {
|
||||
results.values?.let {
|
||||
adapter.items = it as MutableList<Any>
|
||||
}
|
||||
// do not re-bind the search box
|
||||
val count = results.count - 1
|
||||
|
||||
// this tries to update every item except the search field
|
||||
with(adapter) {
|
||||
when {
|
||||
count > prevCount -> {
|
||||
notifyItemRangeInserted(prevCount + 1, count - prevCount)
|
||||
notifyItemRangeChanged(1, prevCount)
|
||||
}
|
||||
count < prevCount -> {
|
||||
notifyItemRangeRemoved(prevCount + 1, prevCount - count)
|
||||
notifyItemRangeChanged(1, count)
|
||||
}
|
||||
else -> {
|
||||
notifyItemRangeChanged(1, count)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prevCount = count
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-4-14.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.messages.utils
|
||||
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.databinding.MessagesListItemSearchBinding
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.models.MessagesSearch
|
||||
|
||||
class SearchTextWatcher(
|
||||
private val b: MessagesListItemSearchBinding,
|
||||
private val filter: MessagesFilter,
|
||||
private val item: MessagesSearch
|
||||
) : TextWatcher {
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit
|
||||
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
item.searchText = s ?: ""
|
||||
filter.filter(s) { count ->
|
||||
if (s.isNullOrBlank())
|
||||
b.searchLayout.helperText = " "
|
||||
else
|
||||
b.searchLayout.helperText =
|
||||
b.root.context.getString(R.string.messages_search_results, count - 1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is SearchTextWatcher
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = b.hashCode()
|
||||
result = 31 * result + filter.hashCode()
|
||||
result = 31 * result + item.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
@ -30,9 +30,13 @@ class MessageViewHolder(
|
||||
private const val TAG = "MessageViewHolder"
|
||||
}
|
||||
|
||||
override fun onBind(activity: AppCompatActivity, app: App, item: MessageFull, position: Int, adapter: MessagesAdapter) {
|
||||
val manager = app.gradesManager
|
||||
|
||||
override fun onBind(
|
||||
activity: AppCompatActivity,
|
||||
app: App,
|
||||
item: MessageFull,
|
||||
position: Int,
|
||||
adapter: MessagesAdapter
|
||||
) {
|
||||
b.messageSubject.text = item.subject
|
||||
b.messageDate.text = Date.fromMillis(item.addedDate).formattedStringShort
|
||||
b.messageAttachmentImage.isVisible = item.hasAttachments
|
||||
@ -55,15 +59,17 @@ class MessageViewHolder(
|
||||
b.messageProfileBackground.setImageBitmap(messageInfo.profileImage)
|
||||
b.messageSender.text = messageInfo.profileName
|
||||
|
||||
item.searchHighlightText?.let { highlight ->
|
||||
item.searchHighlightText?.toString()?.let { highlight ->
|
||||
val colorHighlight = R.attr.colorControlHighlight.resolveAttr(activity)
|
||||
|
||||
b.messageSubject.text = b.messageSubject.text.asSpannable(
|
||||
StyleSpan(Typeface.BOLD), BackgroundColorSpan(colorHighlight),
|
||||
substring = highlight, ignoreCase = true, ignoreDiacritics = true)
|
||||
substring = highlight, ignoreCase = true, ignoreDiacritics = true
|
||||
)
|
||||
b.messageSender.text = b.messageSender.text.asSpannable(
|
||||
StyleSpan(Typeface.BOLD), BackgroundColorSpan(colorHighlight),
|
||||
substring = highlight, ignoreCase = true, ignoreDiacritics = true)
|
||||
substring = highlight, ignoreCase = true, ignoreDiacritics = true
|
||||
)
|
||||
}
|
||||
|
||||
adapter.onItemClick?.let { listener ->
|
||||
|
@ -9,38 +9,43 @@ import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.databinding.MessagesListItemSearchBinding
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.models.MessagesSearch
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.utils.SearchTextWatcher
|
||||
|
||||
class SearchViewHolder(
|
||||
inflater: LayoutInflater,
|
||||
parent: ViewGroup,
|
||||
val b: MessagesListItemSearchBinding = MessagesListItemSearchBinding.inflate(inflater, parent, false)
|
||||
val b: MessagesListItemSearchBinding = MessagesListItemSearchBinding.inflate(
|
||||
inflater,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<MessagesSearch, MessagesAdapter> {
|
||||
companion object {
|
||||
private const val TAG = "SearchViewHolder"
|
||||
}
|
||||
|
||||
override fun onBind(activity: AppCompatActivity, app: App, item: MessagesSearch, position: Int, adapter: MessagesAdapter) {
|
||||
b.searchEdit.removeTextChangedListener(adapter.textWatcher)
|
||||
b.searchEdit.addTextChangedListener(adapter.textWatcher)
|
||||
override fun onBind(
|
||||
activity: AppCompatActivity,
|
||||
app: App,
|
||||
item: MessagesSearch,
|
||||
position: Int,
|
||||
adapter: MessagesAdapter
|
||||
) {
|
||||
val watcher = SearchTextWatcher(b, adapter.filter, item)
|
||||
b.searchEdit.removeTextChangedListener(watcher)
|
||||
|
||||
/*b.searchEdit.setOnKeyboardListener(object : TextInputKeyboardEdit.KeyboardListener {
|
||||
override fun onStateChanged(keyboardEditText: TextInputKeyboardEdit, showing: Boolean) {
|
||||
item.isFocused = showing
|
||||
}
|
||||
})*/
|
||||
|
||||
/*if (b.searchEdit.text.toString() != item.searchText) {
|
||||
if (adapter.items.isEmpty() || adapter.items.size == adapter.allItems.size)
|
||||
b.searchLayout.helperText = " "
|
||||
else
|
||||
b.searchLayout.helperText =
|
||||
b.root.context.getString(R.string.messages_search_results, adapter.items.size - 1)
|
||||
b.searchEdit.setText(item.searchText)
|
||||
b.searchEdit.setSelection(item.searchText.length)
|
||||
}*/
|
||||
|
||||
//b.searchLayout.helperText = app.getString(R.string.messages_search_results, item.count)
|
||||
|
||||
/*if (item.isFocused && !b.searchEdit.isFocused)
|
||||
b.searchEdit.requestFocus()*/
|
||||
b.searchEdit.addTextChangedListener(watcher)
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.utils.colorRes
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
@ -43,6 +45,10 @@ class NotificationsAdapter(
|
||||
val date = Date.fromMillis(item.addedDate).formattedString
|
||||
val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(activity)
|
||||
|
||||
b.notificationIcon.background = IconicsDrawable(app, item.getLargeIcon()).apply {
|
||||
colorRes = R.color.colorPrimary
|
||||
}
|
||||
|
||||
b.title.text = item.text
|
||||
b.profileDate.text = listOf(
|
||||
item.profileName ?: "",
|
||||
|
@ -12,6 +12,8 @@ import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.after
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_LIBRUS
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.REGISTRATION_ENABLED
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.MessagesConfigDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.agenda.AgendaConfigDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.bell.BellSyncConfigDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradesConfigDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.settings.AttendanceConfigDialog
|
||||
@ -58,6 +60,13 @@ class SettingsRegisterCard(util: SettingsUtil) : SettingsCard(util) {
|
||||
}
|
||||
|
||||
override fun getItems() = listOfNotNull(
|
||||
util.createActionItem(
|
||||
text = R.string.menu_agenda_config,
|
||||
icon = CommunityMaterial.Icon.cmd_calendar_outline
|
||||
) {
|
||||
AgendaConfigDialog(activity, reloadOnDismiss = false)
|
||||
},
|
||||
|
||||
util.createActionItem(
|
||||
text = R.string.menu_grades_config,
|
||||
icon = CommunityMaterial.Icon3.cmd_numeric_5_box_outline
|
||||
@ -65,6 +74,13 @@ class SettingsRegisterCard(util: SettingsUtil) : SettingsCard(util) {
|
||||
GradesConfigDialog(activity, reloadOnDismiss = false)
|
||||
},
|
||||
|
||||
util.createActionItem(
|
||||
text = R.string.menu_messages_config,
|
||||
icon = CommunityMaterial.Icon.cmd_calendar_outline
|
||||
) {
|
||||
MessagesConfigDialog(activity, reloadOnDismiss = false)
|
||||
},
|
||||
|
||||
util.createActionItem(
|
||||
text = R.string.menu_attendance_config,
|
||||
icon = CommunityMaterial.Icon.cmd_calendar_remove_outline
|
||||
|
@ -12,6 +12,7 @@ import pl.szczodrzynski.edziennik.ui.dialogs.settings.MiniMenuConfigDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.settings.ThemeChooserDialog
|
||||
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsCard
|
||||
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsUtil
|
||||
import pl.szczodrzynski.edziennik.utils.BigNightUtil
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
@ -36,6 +37,18 @@ class SettingsThemeCard(util: SettingsUtil) : SettingsCard(util) {
|
||||
}
|
||||
else null,
|
||||
|
||||
if (BigNightUtil().isDataWielkanocyNearDzisiaj()) // cool klasa for utility to dzień wielkanocy
|
||||
util.createPropertyItem(
|
||||
text = R.string.settings_theme_eggfall_text,
|
||||
subText = R.string.settings_theme_eggfall_subtext,
|
||||
icon = CommunityMaterial.Icon.cmd_egg_easter,
|
||||
value = configGlobal.ui.eggfall
|
||||
) { _, it ->
|
||||
configGlobal.ui.eggfall = it
|
||||
activity.recreate()
|
||||
}
|
||||
else null,
|
||||
|
||||
util.createActionItem(
|
||||
text = R.string.settings_theme_theme_text,
|
||||
subText = Themes.getThemeNameRes(),
|
||||
|
@ -5,16 +5,16 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.timetable
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.asynclayoutinflater.view.AsyncLayoutInflater
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.marginTop
|
||||
import androidx.core.view.setPadding
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import com.linkedin.android.tachyon.DayView
|
||||
import com.linkedin.android.tachyon.DayViewConfig
|
||||
import kotlinx.coroutines.*
|
||||
@ -24,14 +24,15 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
|
||||
import pl.szczodrzynski.edziennik.databinding.TimetableDayFragmentBinding
|
||||
import pl.szczodrzynski.edziennik.databinding.TimetableLessonBinding
|
||||
import pl.szczodrzynski.edziennik.databinding.TimetableNoTimetableBinding
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.timetable.LessonDetailsDialog
|
||||
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment.Companion.DEFAULT_END_HOUR
|
||||
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment.Companion.DEFAULT_START_HOUR
|
||||
import pl.szczodrzynski.edziennik.utils.ListenerScrollView
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import java.util.*
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.math.min
|
||||
@ -44,33 +45,28 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
|
||||
private lateinit var app: App
|
||||
private lateinit var activity: MainActivity
|
||||
private lateinit var inflater: AsyncLayoutInflater
|
||||
private lateinit var b: TimetableDayFragmentBinding
|
||||
|
||||
private val job: Job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
private var timeIndicatorJob: Job? = null
|
||||
|
||||
private lateinit var date: Date
|
||||
private var startHour = DEFAULT_START_HOUR
|
||||
private var endHour = DEFAULT_END_HOUR
|
||||
private var firstEventMinute = 24 * 60
|
||||
private var paddingTop = 0
|
||||
|
||||
private val manager by lazy { app.timetableManager }
|
||||
private val manager
|
||||
get() = app.timetableManager
|
||||
|
||||
// find SwipeRefreshLayout in the hierarchy
|
||||
private val refreshLayout by lazy { view?.findParentById(R.id.refreshLayout) }
|
||||
// the day ScrollView
|
||||
private val dayScrollDelegate = lazy {
|
||||
val dayScroll = ListenerScrollView(context!!)
|
||||
dayScroll.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
dayScroll.setOnRefreshLayoutEnabledListener { enabled ->
|
||||
refreshLayout?.isEnabled = enabled
|
||||
}
|
||||
dayScroll
|
||||
}
|
||||
private val dayScroll by dayScrollDelegate
|
||||
// the lesson DayView
|
||||
|
||||
private val dayView by lazy {
|
||||
val dayView = DayView(context!!, DayViewConfig(
|
||||
val dayView = DayView(activity, DayViewConfig(
|
||||
startHour = startHour,
|
||||
endHour = endHour,
|
||||
dividerHeight = 1.dp,
|
||||
@ -82,37 +78,33 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
|
||||
eventMargin = 2.dp
|
||||
), true)
|
||||
dayView.setPadding(10.dp)
|
||||
dayScroll.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
dayScroll.addView(dayView)
|
||||
dayView
|
||||
return@lazy dayView
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
activity = (getActivity() as MainActivity?) ?: return null
|
||||
context ?: return null
|
||||
app = activity.application as App
|
||||
this.inflater = AsyncLayoutInflater(context!!)
|
||||
this.inflater = AsyncLayoutInflater(requireContext())
|
||||
|
||||
date = arguments?.getInt("date")?.let { Date.fromValue(it) } ?: Date.getToday()
|
||||
startHour = arguments?.getInt("startHour") ?: DEFAULT_START_HOUR
|
||||
endHour = arguments?.getInt("endHour") ?: DEFAULT_END_HOUR
|
||||
return FrameLayout(activity).apply {
|
||||
layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
addView(ProgressBar(activity).apply {
|
||||
layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER)
|
||||
})
|
||||
}
|
||||
|
||||
b = TimetableDayFragmentBinding.inflate(inflater, null, false)
|
||||
return b.root
|
||||
}
|
||||
|
||||
override fun onPageCreated(): Boolean {
|
||||
// observe lesson database
|
||||
app.db.timetableDao().getAllForDate(App.profileId, date).observe(this, Observer { lessons ->
|
||||
app.db.timetableDao().getAllForDate(App.profileId, date).observe(this) { lessons ->
|
||||
launch {
|
||||
val events = withContext(Dispatchers.Default) {
|
||||
app.db.eventDao().getAllByDateNow(App.profileId, date)
|
||||
}
|
||||
processLessonList(lessons, events)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@ -120,9 +112,10 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
|
||||
private fun processLessonList(lessons: List<LessonFull>, events: List<EventFull>) {
|
||||
// no lessons - timetable not downloaded yet
|
||||
if (lessons.isEmpty()) {
|
||||
inflater.inflate(R.layout.timetable_no_timetable, view as FrameLayout?) { view, _, parent ->
|
||||
parent?.removeAllViews()
|
||||
parent?.addView(view)
|
||||
inflater.inflate(R.layout.timetable_no_timetable, b.root) { view, _, _ ->
|
||||
b.root.removeAllViews()
|
||||
b.root.addView(view)
|
||||
|
||||
val b = TimetableNoTimetableBinding.bind(view)
|
||||
val weekStart = date.weekStart.stringY_m_d
|
||||
b.noTimetableSync.onClick {
|
||||
@ -143,9 +136,9 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
|
||||
}
|
||||
// one lesson indicating a day without lessons
|
||||
if (lessons.size == 1 && lessons[0].type == Lesson.TYPE_NO_LESSONS) {
|
||||
inflater.inflate(R.layout.timetable_no_lessons, view as FrameLayout?) { view, _, parent ->
|
||||
parent?.removeAllViews()
|
||||
parent?.addView(view)
|
||||
inflater.inflate(R.layout.timetable_no_lessons, b.root) { view, _, _ ->
|
||||
b.root.removeAllViews()
|
||||
b.root.addView(view)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -157,12 +150,12 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
|
||||
return
|
||||
}
|
||||
|
||||
// clear the root view and add the ScrollView
|
||||
(view as FrameLayout?)?.removeAllViews()
|
||||
(view as FrameLayout?)?.addView(dayScroll)
|
||||
b.scrollView.isVisible = true
|
||||
b.dayFrame.removeView(b.dayView)
|
||||
b.dayFrame.addView(dayView, 0)
|
||||
|
||||
// Inflate a label view for each hour the day view will display
|
||||
val hourLabelViews = ArrayList<View>()
|
||||
val hourLabelViews = mutableListOf<View>()
|
||||
for (i in dayView.startHour..dayView.endHour) {
|
||||
if (!isAdded)
|
||||
continue
|
||||
@ -171,6 +164,11 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
|
||||
hourLabelViews.add(hourLabelView)
|
||||
}
|
||||
dayView.setHourLabelViews(hourLabelViews)
|
||||
// measure dayView top padding needed for the timeIndicator
|
||||
hourLabelViews.getOrNull(0)?.let {
|
||||
it.measure(0, 0)
|
||||
paddingTop = it.measuredHeight / 2 + dayView.paddingTop
|
||||
}
|
||||
|
||||
lessons.forEach { it.showAsUnseen = !it.seen }
|
||||
|
||||
@ -201,8 +199,12 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
|
||||
|
||||
// Try to recycle an existing event view if there are enough left, otherwise inflate
|
||||
// a new one
|
||||
val eventView = (if (remaining > 0) recycled?.get(--remaining) else layoutInflater.inflate(R.layout.timetable_lesson, dayView, false))
|
||||
?: continue
|
||||
val eventView =
|
||||
(if (remaining > 0) recycled?.get(--remaining) else layoutInflater.inflate(
|
||||
R.layout.timetable_lesson,
|
||||
dayView,
|
||||
false
|
||||
)) ?: continue
|
||||
val lb = TimetableLessonBinding.bind(eventView)
|
||||
eventViews += eventView
|
||||
|
||||
@ -290,16 +292,50 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
|
||||
eventTimeRanges.add(DayView.EventTimeRange(startMinute, endMinute))
|
||||
}
|
||||
|
||||
updateTimeIndicator()
|
||||
|
||||
dayView.setEventViews(eventViews, eventTimeRanges)
|
||||
val firstEventTop = (firstEventMinute - dayView.startHour * 60) * dayView.minuteHeight
|
||||
dayScroll.scrollTo(0, firstEventTop.toInt())
|
||||
b.scrollView.scrollTo(0, firstEventTop.toInt())
|
||||
|
||||
b.progressBar.isVisible = false
|
||||
}
|
||||
|
||||
private fun updateTimeIndicator() {
|
||||
val time = Time.getNow()
|
||||
val isTimeInView =
|
||||
date == Date.getToday() && time.hour in dayView.startHour..dayView.endHour
|
||||
|
||||
b.timeIndicator.isVisible = isTimeInView
|
||||
b.timeIndicatorMarker.isVisible = isTimeInView
|
||||
if (isTimeInView) {
|
||||
val startTime = Time(dayView.startHour, 0, 0)
|
||||
val seconds = time.inSeconds - startTime.inSeconds * 1f
|
||||
b.timeIndicator.updateLayoutParams<FrameLayout.LayoutParams> {
|
||||
topMargin = (seconds * dayView.minuteHeight / 60f).toInt() + paddingTop
|
||||
}
|
||||
b.timeIndicatorMarker.updateLayoutParams<FrameLayout.LayoutParams> {
|
||||
topMargin = b.timeIndicator.marginTop - (16.dp / 2) + (1.dp / 2)
|
||||
}
|
||||
}
|
||||
|
||||
if (timeIndicatorJob == null) {
|
||||
timeIndicatorJob = startCoroutineTimer(repeatMillis = 30000) {
|
||||
updateTimeIndicator()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (dayScrollDelegate.isInitialized()) {
|
||||
val firstEventTop = (firstEventMinute - dayView.startHour * 60) * dayView.minuteHeight
|
||||
dayScroll.scrollTo(0, firstEventTop.toInt())
|
||||
b.scrollView.scrollTo(0, firstEventTop.toInt())
|
||||
updateTimeIndicator()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
timeIndicatorJob?.cancel()
|
||||
timeIndicatorJob = null
|
||||
}
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ class DateDropdown : TextInputDropDown {
|
||||
}
|
||||
}
|
||||
|
||||
fun pickerDialog() {
|
||||
private fun pickerDialog() {
|
||||
val date = getSelected() as? Date ?: Date.getToday()
|
||||
|
||||
MaterialDatePicker.Builder.datePicker()
|
||||
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-4-14.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.views
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import pl.szczodrzynski.edziennik.data.db.AppDb
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.EventType
|
||||
import pl.szczodrzynski.edziennik.utils.TextInputDropDown
|
||||
|
||||
class EventTypeDropdown : TextInputDropDown {
|
||||
constructor(context: Context) : super(context)
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
|
||||
|
||||
lateinit var db: AppDb
|
||||
var profileId: Int = 0
|
||||
var onTypeSelected: ((eventType: EventType) -> Unit)? = null
|
||||
|
||||
override fun create(context: Context) {
|
||||
super.create(context)
|
||||
isEnabled = false
|
||||
}
|
||||
|
||||
suspend fun loadItems() {
|
||||
val types = withContext(Dispatchers.Default) {
|
||||
val list = mutableListOf<Item>()
|
||||
|
||||
var types = db.eventTypeDao().getAllNow(profileId)
|
||||
|
||||
if (types.none { it.id in -1L..10L }) {
|
||||
types = db.eventTypeDao().addDefaultTypes(context, profileId)
|
||||
}
|
||||
|
||||
list += types.map {
|
||||
Item(it.id, it.name, tag = it, icon = IconicsDrawable(context).apply {
|
||||
icon = CommunityMaterial.Icon.cmd_circle
|
||||
sizeDp = 24
|
||||
colorInt = it.color
|
||||
})
|
||||
}
|
||||
|
||||
list
|
||||
}
|
||||
|
||||
clear().append(types)
|
||||
isEnabled = true
|
||||
|
||||
setOnChangeListener {
|
||||
when (it.tag) {
|
||||
is EventType -> {
|
||||
// selected an event type
|
||||
onTypeSelected?.invoke(it.tag)
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select an event type by the [typeId].
|
||||
*/
|
||||
fun selectType(typeId: Long) = select(typeId)
|
||||
|
||||
/**
|
||||
* Select an event type by the [typeId] **if it's not selected yet**.
|
||||
*/
|
||||
fun selectDefault(typeId: Long?) {
|
||||
if (typeId == null || selected != null)
|
||||
return
|
||||
selectType(typeId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently selected event type.
|
||||
* ### Returns:
|
||||
* - null if no valid type is selected
|
||||
* - [EventType] - the selected event type
|
||||
*/
|
||||
fun getSelected(): EventType? {
|
||||
return when (selected?.tag) {
|
||||
is EventType -> selected?.tag as EventType
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ import kotlinx.coroutines.withContext
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.crc16
|
||||
import pl.szczodrzynski.edziennik.data.db.AppDb
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Subject
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.input
|
||||
import pl.szczodrzynski.edziennik.utils.TextInputDropDown
|
||||
|
||||
@ -40,7 +41,7 @@ class SubjectDropdown : TextInputDropDown {
|
||||
var showNoSubject = true
|
||||
var showCustomSubject = false
|
||||
var customSubjectName = ""
|
||||
var onSubjectSelected: ((subjectId: Long?) -> Unit)? = null
|
||||
var onSubjectSelected: ((subject: Subject?) -> Unit)? = null
|
||||
var onCustomSubjectSelected: ((subjectName: String) -> Unit)? = null
|
||||
|
||||
override fun create(context: Context) {
|
||||
@ -73,7 +74,7 @@ class SubjectDropdown : TextInputDropDown {
|
||||
list += subjects.map { Item(
|
||||
it.id,
|
||||
it.longName,
|
||||
tag = it.id
|
||||
tag = it
|
||||
) }
|
||||
|
||||
list
|
||||
@ -91,10 +92,11 @@ class SubjectDropdown : TextInputDropDown {
|
||||
}
|
||||
-1L -> {
|
||||
// no subject
|
||||
deselect()
|
||||
onSubjectSelected?.invoke(null)
|
||||
true
|
||||
false
|
||||
}
|
||||
is Long -> {
|
||||
is Subject -> {
|
||||
// selected a subject
|
||||
onSubjectSelected?.invoke(it.tag)
|
||||
true
|
||||
@ -104,7 +106,7 @@ class SubjectDropdown : TextInputDropDown {
|
||||
}
|
||||
}
|
||||
|
||||
fun customNameDialog() {
|
||||
private fun customNameDialog() {
|
||||
activity ?: return
|
||||
MaterialAlertDialogBuilder(activity!!)
|
||||
.setTitle("Własny przedmiot")
|
||||
@ -127,32 +129,37 @@ class SubjectDropdown : TextInputDropDown {
|
||||
.show()
|
||||
}
|
||||
|
||||
fun selectSubject(subjectId: Long) {
|
||||
if (select(subjectId) == null)
|
||||
select(Item(
|
||||
subjectId,
|
||||
"nieznany przedmiot ($subjectId)",
|
||||
tag = subjectId
|
||||
))
|
||||
/**
|
||||
* Select a subject by the [subjectId].
|
||||
*/
|
||||
fun selectSubject(subjectId: Long): Item? {
|
||||
if (subjectId == -1L) {
|
||||
deselect()
|
||||
return null
|
||||
}
|
||||
return select(subjectId)
|
||||
}
|
||||
|
||||
fun selectDefault(subjectId: Long?) {
|
||||
/**
|
||||
* Select a subject by the [subjectId] **if it's not selected yet**.
|
||||
*/
|
||||
fun selectDefault(subjectId: Long?): Item? {
|
||||
if (subjectId == null || selected != null)
|
||||
return
|
||||
selectSubject(subjectId)
|
||||
return null
|
||||
return selectSubject(subjectId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently selected subject.
|
||||
* ### Returns:
|
||||
* - null if no valid subject is selected
|
||||
* - [Long] - the selected subject's ID
|
||||
* - [Subject] - the selected subject
|
||||
* - [String] - a custom subject name entered, if [showCustomSubject] == true
|
||||
*/
|
||||
fun getSelected(): Any? {
|
||||
return when (selected?.tag) {
|
||||
-1L -> null
|
||||
is Long -> selected?.tag as Long
|
||||
is Subject -> selected?.tag as Subject
|
||||
is String -> selected?.tag as String
|
||||
else -> null
|
||||
}
|
||||
|
@ -5,13 +5,12 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.views
|
||||
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.AppDb
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||
import pl.szczodrzynski.edziennik.utils.TextInputDropDown
|
||||
|
||||
class TeacherDropdown : TextInputDropDown {
|
||||
@ -19,22 +18,10 @@ class TeacherDropdown : TextInputDropDown {
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
|
||||
|
||||
private val activity: AppCompatActivity?
|
||||
get() {
|
||||
var context: Context? = context ?: return null
|
||||
if (context is AppCompatActivity) return context
|
||||
while (context is ContextWrapper) {
|
||||
if (context is AppCompatActivity)
|
||||
return context
|
||||
context = context.baseContext
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
lateinit var db: AppDb
|
||||
var profileId: Int = 0
|
||||
var showNoTeacher = true
|
||||
var onTeacherSelected: ((teacherId: Long?) -> Unit)? = null
|
||||
var onTeacherSelected: ((teacher: Teacher?) -> Unit)? = null
|
||||
|
||||
override fun create(context: Context) {
|
||||
super.create(context)
|
||||
@ -58,7 +45,7 @@ class TeacherDropdown : TextInputDropDown {
|
||||
list += teachers.map { Item(
|
||||
it.id,
|
||||
it.fullName,
|
||||
tag = it.id
|
||||
tag = it
|
||||
) }
|
||||
|
||||
list
|
||||
@ -71,10 +58,11 @@ class TeacherDropdown : TextInputDropDown {
|
||||
when (it.tag) {
|
||||
-1L -> {
|
||||
// no teacher
|
||||
deselect()
|
||||
onTeacherSelected?.invoke(null)
|
||||
true
|
||||
false
|
||||
}
|
||||
is Long -> {
|
||||
is Teacher -> {
|
||||
// selected a teacher
|
||||
onTeacherSelected?.invoke(it.tag)
|
||||
true
|
||||
@ -84,31 +72,36 @@ class TeacherDropdown : TextInputDropDown {
|
||||
}
|
||||
}
|
||||
|
||||
fun selectTeacher(teacherId: Long) {
|
||||
if (select(teacherId) == null)
|
||||
select(Item(
|
||||
teacherId,
|
||||
"nieznany nauczyciel ($teacherId)",
|
||||
tag = teacherId
|
||||
))
|
||||
/**
|
||||
* Select a teacher by the [teacherId].
|
||||
*/
|
||||
fun selectTeacher(teacherId: Long): Item? {
|
||||
if (teacherId == -1L) {
|
||||
deselect()
|
||||
return null
|
||||
}
|
||||
return select(teacherId)
|
||||
}
|
||||
|
||||
fun selectDefault(teacherId: Long?) {
|
||||
/**
|
||||
* Select a teacher by the [teacherId] **if it's not selected yet**.
|
||||
*/
|
||||
fun selectDefault(teacherId: Long?): Item? {
|
||||
if (teacherId == null || selected != null)
|
||||
return
|
||||
selectTeacher(teacherId)
|
||||
return null
|
||||
return selectTeacher(teacherId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently selected teacher.
|
||||
* ### Returns:
|
||||
* - null if no valid teacher is selected
|
||||
* - [Long] - the selected teacher's ID
|
||||
* - [Teacher] - the selected teacher
|
||||
*/
|
||||
fun getSelected(): Long? {
|
||||
fun getSelected(): Teacher? {
|
||||
return when (selected?.tag) {
|
||||
-1L -> null
|
||||
is Long -> selected?.tag as Long
|
||||
is Teacher -> selected?.tag as Teacher
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,7 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.views
|
||||
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
@ -20,22 +18,10 @@ class TeamDropdown : TextInputDropDown {
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
|
||||
|
||||
private val activity: AppCompatActivity?
|
||||
get() {
|
||||
var context: Context? = context ?: return null
|
||||
if (context is AppCompatActivity) return context
|
||||
while (context is ContextWrapper) {
|
||||
if (context is AppCompatActivity)
|
||||
return context
|
||||
context = context.baseContext
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
lateinit var db: AppDb
|
||||
var profileId: Int = 0
|
||||
var showNoTeam = true
|
||||
var onTeamSelected: ((teamId: Long?) -> Unit)? = null
|
||||
var onTeamSelected: ((team: Team?) -> Unit)? = null
|
||||
|
||||
override fun create(context: Context) {
|
||||
super.create(context)
|
||||
@ -59,7 +45,7 @@ class TeamDropdown : TextInputDropDown {
|
||||
list += teams.map { Item(
|
||||
it.id,
|
||||
it.name,
|
||||
tag = it.id
|
||||
tag = it
|
||||
) }
|
||||
|
||||
list
|
||||
@ -72,10 +58,11 @@ class TeamDropdown : TextInputDropDown {
|
||||
when (it.tag) {
|
||||
-1L -> {
|
||||
// no team
|
||||
deselect()
|
||||
onTeamSelected?.invoke(null)
|
||||
true
|
||||
false
|
||||
}
|
||||
is Long -> {
|
||||
is Team -> {
|
||||
// selected a team
|
||||
onTeamSelected?.invoke(it.tag)
|
||||
true
|
||||
@ -85,21 +72,29 @@ class TeamDropdown : TextInputDropDown {
|
||||
}
|
||||
}
|
||||
|
||||
fun selectTeam(teamId: Long) {
|
||||
if (select(teamId) == null)
|
||||
select(Item(
|
||||
teamId,
|
||||
"nieznana grupa ($teamId)",
|
||||
tag = teamId
|
||||
))
|
||||
/**
|
||||
* Select a teacher by the [teamId].
|
||||
*/
|
||||
fun selectTeam(teamId: Long): Item? {
|
||||
if (teamId == -1L) {
|
||||
deselect()
|
||||
return null
|
||||
}
|
||||
return select(teamId)
|
||||
}
|
||||
|
||||
fun selectDefault(teamId: Long?) {
|
||||
/**
|
||||
* Select a team by the [teamId] **if it's not selected yet**.
|
||||
*/
|
||||
fun selectDefault(teamId: Long?): Item? {
|
||||
if (teamId == null || selected != null)
|
||||
return
|
||||
selectTeam(teamId)
|
||||
return null
|
||||
return selectTeam(teamId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a team of the [Team.TYPE_CLASS] type.
|
||||
*/
|
||||
fun selectTeamClass() {
|
||||
select(items.singleOrNull {
|
||||
it.tag is Team && it.tag.type == Team.TYPE_CLASS
|
||||
@ -110,12 +105,12 @@ class TeamDropdown : TextInputDropDown {
|
||||
* Get the currently selected team.
|
||||
* ### Returns:
|
||||
* - null if no valid team is selected
|
||||
* - [Long] - the team's ID
|
||||
* - [Team] - the selected team
|
||||
*/
|
||||
fun getSelected(): Any? {
|
||||
fun getSelected(): Team? {
|
||||
return when (selected?.tag) {
|
||||
-1L -> null
|
||||
is Long -> selected?.tag as Long
|
||||
is Team -> selected?.tag as Team
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ class TimeDropdown : TextInputDropDown {
|
||||
return !noTimetable
|
||||
}
|
||||
|
||||
fun pickerDialog() {
|
||||
private fun pickerDialog() {
|
||||
val time = (getSelected() as? Pair<*, *>)?.first as? Time ?: Time.getNow()
|
||||
|
||||
MaterialTimePicker.Builder()
|
||||
|
@ -45,6 +45,7 @@ class WidgetNotificationsFactory(val app: App, val config: WidgetConfig) : Remot
|
||||
getLong("id") ?: 0,
|
||||
getString("title") ?: "",
|
||||
getString("text") ?: "",
|
||||
getString("textLong"),
|
||||
getInt("type") ?: 0,
|
||||
getInt("profileId"),
|
||||
getString("profileName"),
|
||||
|
@ -7,6 +7,8 @@
|
||||
package pl.szczodrzynski.edziennik.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
@ -19,141 +21,268 @@ import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.view.menu.MenuBuilder
|
||||
import androidx.appcompat.view.menu.MenuPopupHelper
|
||||
import pl.szczodrzynski.edziennik.Intent
|
||||
import pl.szczodrzynski.edziennik.copyToClipboard
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.api.Regexes
|
||||
import pl.szczodrzynski.edziennik.get
|
||||
import pl.szczodrzynski.edziennik.getTextPosition
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
object BetterLink {
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
fun attach(textView: TextView, onActionSelected: (() -> Unit)? = null) {
|
||||
textView.autoLinkMask = Linkify.WEB_URLS or Linkify.EMAIL_ADDRESSES
|
||||
BetterLinkMovementMethod.linkify(textView.autoLinkMask, textView).setOnLinkClickListener { v, span: BetterLinkMovementMethod.ClickableSpanWithText ->
|
||||
val url = span.text()
|
||||
val c = v.context
|
||||
/**
|
||||
* Used in conjunction with the item's ID to execute the
|
||||
* [attach]'s onActionSelected listener when the item is
|
||||
* clicked.
|
||||
*/
|
||||
private const val FLAG_ACTION = 0x8000
|
||||
|
||||
val s = v.text as Spanned
|
||||
val start = s.getSpanStart(span.span())
|
||||
val end = s.getSpanEnd(span.span())
|
||||
private fun MenuBuilder.setTitle(title: CharSequence): MenuBuilder {
|
||||
this::class.java.getDeclaredMethod("setHeaderTitleInt", CharSequence::class.java).let {
|
||||
it.isAccessible = true
|
||||
it.invoke(this, title)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
val parent = v.rootView.findViewById<ViewGroup>(android.R.id.content)
|
||||
private fun MenuItem.addListener(listener: (item: MenuItem) -> Boolean): MenuItem {
|
||||
this::class.java.getDeclaredField("mClickListener").let {
|
||||
it.isAccessible = true
|
||||
val oldListener = it.get(this) as? MenuItem.OnMenuItemClickListener
|
||||
it.set(this, object : MenuItem.OnMenuItemClickListener {
|
||||
override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
oldListener?.onMenuItemClick(item)
|
||||
return listener(item)
|
||||
}
|
||||
})
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
private fun createUrlItems(menu: MenuBuilder, context: Context, url: String) {
|
||||
menu.setTitle(url)
|
||||
menu.add(
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
"Otwórz w przeglądarce"
|
||||
).setOnMenuItemClickListener {
|
||||
Utils.openUrl(context, url)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun createMailtoItems(menu: MenuBuilder, context: Context, url: String) {
|
||||
menu.add(
|
||||
1,
|
||||
3,
|
||||
3,
|
||||
"Napisz e-mail"
|
||||
).setOnMenuItemClickListener {
|
||||
Utils.openUrl(context, url)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDateItems(menu: MenuBuilder, context: Context, date: Date?) {
|
||||
date ?: return
|
||||
menu.setTitle(date.formattedString)
|
||||
menu.add(
|
||||
1,
|
||||
4 or FLAG_ACTION,
|
||||
4,
|
||||
"Utwórz wydarzenie"
|
||||
).setOnMenuItemClickListener {
|
||||
val intent = Intent(
|
||||
Intent.ACTION_MAIN,
|
||||
"action" to "createManualEvent",
|
||||
"eventDate" to date.stringY_m_d
|
||||
)
|
||||
context.sendBroadcast(intent)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun createTeacherItems(menu: MenuBuilder, context: Context, teacherId: Long, fullName: String) {
|
||||
menu.setTitle(fullName)
|
||||
menu.add(
|
||||
1,
|
||||
5 or FLAG_ACTION,
|
||||
5,
|
||||
"Napisz wiadomość"
|
||||
).setOnMenuItemClickListener {
|
||||
val intent = Intent(
|
||||
Intent.ACTION_MAIN,
|
||||
"fragmentId" to MainActivity.TARGET_MESSAGES_COMPOSE,
|
||||
"messageRecipientId" to teacherId
|
||||
)
|
||||
context.sendBroadcast(intent)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun onClickListener(
|
||||
view: TextView,
|
||||
span: BetterLinkMovementMethod.ClickableSpanWithText,
|
||||
onActionSelected: (() -> Unit)?
|
||||
): Boolean {
|
||||
val context = view.context
|
||||
|
||||
val spanned = view.text as Spanned
|
||||
val start = spanned.getSpanStart(span.span())
|
||||
val end = spanned.getSpanEnd(span.span())
|
||||
|
||||
val parent = view.rootView.findViewById<ViewGroup>(android.R.id.content)
|
||||
val parentLocation = intArrayOf(0, 0)
|
||||
parent.getLocationOnScreen(parentLocation)
|
||||
|
||||
val rect = textView.getTextPosition(start..end)
|
||||
val rect = view.getTextPosition(start..end)
|
||||
|
||||
val view = View(c)
|
||||
view.layoutParams = ViewGroup.LayoutParams(rect.width(), rect.height())
|
||||
view.setBackgroundColor(Color.TRANSPARENT)
|
||||
val popupView = View(context)
|
||||
popupView.layoutParams = ViewGroup.LayoutParams(rect.width(), rect.height())
|
||||
popupView.setBackgroundColor(Color.TRANSPARENT)
|
||||
|
||||
parent.addView(view)
|
||||
parent.addView(popupView)
|
||||
|
||||
view.x = rect.left.toFloat() - parentLocation[0]
|
||||
view.y = rect.top.toFloat() - parentLocation[1]
|
||||
popupView.x = rect.left.toFloat() - parentLocation[0]
|
||||
popupView.y = rect.top.toFloat() - parentLocation[1]
|
||||
|
||||
val menu = MenuBuilder(c)
|
||||
val helper = MenuPopupHelper(c, menu, view)
|
||||
val menu = MenuBuilder(context)
|
||||
val helper = MenuPopupHelper(context, menu, popupView)
|
||||
val popup = helper.popup
|
||||
|
||||
var menuTitle = url.substringAfter(":")
|
||||
var date: Date? = null
|
||||
val spanUrl = span.text()
|
||||
val spanParameter = spanUrl.substringAfter(":")
|
||||
val spanText = spanned.substring(start, end)
|
||||
|
||||
var urlItem: MenuItem? = null
|
||||
var createEventItem: MenuItem? = null
|
||||
//var goToTimetableItem: MenuItem? = null // TODO 2020-03-19: implement this
|
||||
var mailItem: MenuItem? = null
|
||||
var copyItem: MenuItem? = null
|
||||
//goToTimetableItem = menu.add(1, 11, 3, "Idź do planu lekcji")
|
||||
|
||||
// create appropriate items for spans
|
||||
when {
|
||||
url.startsWith("mailto:") -> {
|
||||
mailItem = menu.add(1, 20, 2, "Napisz e-mail")
|
||||
}
|
||||
url.startsWith("dateYmd:") -> {
|
||||
createEventItem = menu.add(1, 10, 2, "Utwórz wydarzenie")
|
||||
//goToTimetableItem = menu.add(1, 11, 3, "Idź do planu lekcji")
|
||||
date = parseDateYmd(menuTitle)
|
||||
}
|
||||
url.startsWith("dateDmy:") -> {
|
||||
createEventItem = menu.add(1, 10, 2, "Utwórz wydarzenie")
|
||||
//goToTimetableItem = menu.add(1, 11, 3, "Idź do planu lekcji")
|
||||
date = parseDateDmy(menuTitle)
|
||||
}
|
||||
url.startsWith("dateAbs:") -> {
|
||||
createEventItem = menu.add(1, 10, 2, "Utwórz wydarzenie")
|
||||
//goToTimetableItem = menu.add(1, 11, 3, "Idź do planu lekcji")
|
||||
date = parseDateAbs(menuTitle)
|
||||
}
|
||||
url.startsWith("dateRel:") -> {
|
||||
createEventItem = menu.add(1, 10, 2, "Utwórz wydarzenie")
|
||||
//goToTimetableItem = menu.add(1, 11, 3, "Idź do planu lekcji")
|
||||
date = parseDateRel(menuTitle)
|
||||
}
|
||||
else -> {
|
||||
urlItem = menu.add(1, 1, 2, "Otwórz w przeglądarce")
|
||||
menuTitle = url
|
||||
}
|
||||
}
|
||||
copyItem = menu.add(1, 1000, 1000, "Kopiuj tekst")
|
||||
|
||||
helper.setOnDismissListener { parent.removeView(view) }
|
||||
|
||||
urlItem?.setOnMenuItemClickListener { Utils.openUrl(c, url); true }
|
||||
mailItem?.setOnMenuItemClickListener { Utils.openUrl(c, url); true }
|
||||
copyItem?.setOnMenuItemClickListener { menuTitle.copyToClipboard(c); true }
|
||||
createEventItem?.setOnMenuItemClickListener {
|
||||
onActionSelected?.invoke()
|
||||
val intent = Intent(
|
||||
android.content.Intent.ACTION_MAIN,
|
||||
"action" to "createManualEvent",
|
||||
"eventDate" to date?.stringY_m_d
|
||||
spanUrl.startsWith("mailto:") -> createMailtoItems(menu, context, spanUrl)
|
||||
spanUrl.startsWith("dateYmd:") -> createDateItems(menu, context, parseDateYmd(spanParameter))
|
||||
spanUrl.startsWith("dateDmy:") -> createDateItems(menu, context, parseDateDmy(spanParameter))
|
||||
spanUrl.startsWith("dateAbs:") -> createDateItems(menu, context, parseDateAbs(spanParameter))
|
||||
spanUrl.startsWith("dateRel:") -> createDateItems(menu, context, parseDateRel(spanParameter))
|
||||
spanUrl.startsWith("teacher:") -> createTeacherItems(
|
||||
menu,
|
||||
context,
|
||||
teacherId = spanParameter.toLongOrNull() ?: -1,
|
||||
fullName = spanText
|
||||
)
|
||||
c.sendBroadcast(intent)
|
||||
else -> createUrlItems(menu, context, spanUrl)
|
||||
}
|
||||
menu.add(1, 1000, 1000, "Kopiuj tekst").setOnMenuItemClickListener {
|
||||
spanParameter.copyToClipboard(context)
|
||||
true
|
||||
}
|
||||
|
||||
menu::class.java.getDeclaredMethod("setHeaderTitleInt", CharSequence::class.java).let {
|
||||
it.isAccessible = true
|
||||
it.invoke(menu, menuTitle)
|
||||
helper.setOnDismissListener { parent.removeView(popupView) }
|
||||
|
||||
menu.visibleItems.forEach { item ->
|
||||
if ((item.itemId and FLAG_ACTION) != FLAG_ACTION)
|
||||
return@forEach
|
||||
item.addListener {
|
||||
onActionSelected?.invoke()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
popup::class.java.getDeclaredField("mShowTitle").let {
|
||||
it.isAccessible = true
|
||||
it.set(popup, true)
|
||||
}
|
||||
helper::class.java.getDeclaredMethod("showPopup", Int::class.java, Int::class.java, Boolean::class.java, Boolean::class.java).let {
|
||||
helper::class.java.getDeclaredMethod(
|
||||
"showPopup",
|
||||
Int::class.java,
|
||||
Int::class.java,
|
||||
Boolean::class.java,
|
||||
Boolean::class.java
|
||||
).let {
|
||||
it.isAccessible = true
|
||||
it.invoke(helper, 0, 0, false, true)
|
||||
}
|
||||
true
|
||||
return true
|
||||
}
|
||||
|
||||
val spanned = textView.text as? Spannable ?: {
|
||||
SpannableString(textView.text)
|
||||
}()
|
||||
fun attach(
|
||||
textView: TextView,
|
||||
teachers: Map<Long, String>? = null,
|
||||
onActionSelected: (() -> Unit)? = null
|
||||
) {
|
||||
textView.autoLinkMask = Linkify.WEB_URLS or Linkify.EMAIL_ADDRESSES
|
||||
|
||||
BetterLinkMovementMethod
|
||||
.linkify(textView.autoLinkMask, textView)
|
||||
.setOnLinkClickListener { view, span ->
|
||||
onClickListener(view, span, onActionSelected)
|
||||
}
|
||||
|
||||
textView.addTextChangedListener {
|
||||
attachSpan(textView, teachers)
|
||||
}
|
||||
|
||||
attachSpan(textView, teachers)
|
||||
}
|
||||
|
||||
private fun attachSpan(
|
||||
textView: TextView,
|
||||
teachers: Map<Long, String>? = null
|
||||
) {
|
||||
val spanned = textView.text as? Spannable ?: SpannableString(textView.text)
|
||||
|
||||
teachers?.forEach { (id, fullName) ->
|
||||
val index = textView.text.indexOf(fullName)
|
||||
if (index == -1)
|
||||
return@forEach
|
||||
val span = URLSpan("teacher:$id")
|
||||
spanned.setSpan(
|
||||
span,
|
||||
index,
|
||||
index + fullName.length,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
|
||||
Regexes.LINKIFY_DATE_YMD.findAll(textView.text).forEach { match ->
|
||||
val span = URLSpan("dateYmd:" + match.value)
|
||||
spanned.setSpan(span, match.range.first, match.range.last + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
spanned.setSpan(
|
||||
span,
|
||||
match.range.first,
|
||||
match.range.last + 1,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
Regexes.LINKIFY_DATE_DMY.findAll(textView.text).forEach { match ->
|
||||
val span = URLSpan("dateDmy:" + match.value)
|
||||
spanned.setSpan(span, match.range.first, match.range.last + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
spanned.setSpan(
|
||||
span,
|
||||
match.range.first,
|
||||
match.range.last + 1,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
Regexes.LINKIFY_DATE_ABSOLUTE.findAll(textView.text).forEach { match ->
|
||||
val span = URLSpan("dateAbs:" + match.value)
|
||||
spanned.setSpan(span, match.range.first, match.range.last + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
spanned.setSpan(
|
||||
span,
|
||||
match.range.first,
|
||||
match.range.last + 1,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
Regexes.LINKIFY_DATE_RELATIVE.findAll(textView.text).forEach { match ->
|
||||
val span = URLSpan("dateRel:" + match.value)
|
||||
spanned.setSpan(span, match.range.first, match.range.last + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
spanned.setSpan(
|
||||
span,
|
||||
match.range.first,
|
||||
match.range.last + 1,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
//Linkify.addLinks(textView, LINKIFY_DATE_ABSOLUTE.toPattern(), "dateAbs:")
|
||||
//Linkify.addLinks(textView, LINKIFY_DATE_RELATIVE.toPattern(), "dateRel:")
|
||||
}
|
||||
|
||||
private val monthNames = listOf("sty", "lut", "mar", "kwi", "maj", "cze", "lip", "sie", "wrz", "paź", "lis", "gru")
|
||||
private val monthNames =
|
||||
listOf("sty", "lut", "mar", "kwi", "maj", "cze", "lip", "sie", "wrz", "paź", "lis", "gru")
|
||||
|
||||
private fun parseDateYmd(text: String): Date? {
|
||||
return Regexes.LINKIFY_DATE_YMD.find(text)?.let {
|
||||
@ -163,6 +292,7 @@ object BetterLink {
|
||||
Date(year, month, day)
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseDateDmy(text: String): Date? {
|
||||
return Regexes.LINKIFY_DATE_DMY.find(text)?.let {
|
||||
val day = it[1].toIntOrNull() ?: 1
|
||||
@ -194,7 +324,7 @@ object BetterLink {
|
||||
else -> 1
|
||||
}
|
||||
|
||||
date.stepForward(0, 0, amount*unitInDays)
|
||||
date.stepForward(0, 0, amount * unitInDays)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-4-8.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.utils
|
||||
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
// Obliczanie daty wielkanocy - algorytm Gaussa
|
||||
// www.algorytm.org
|
||||
// (c) 2008 by Tomasz Lubinski
|
||||
// http://www.algorytm.org/przetwarzanie-dat/wyznaczanie-daty-wielkanocy-algortym-gaussa/dwg-j.html
|
||||
|
||||
class BigNightUtil {
|
||||
|
||||
/* Pobierz wartosc A z tabeli lat */
|
||||
private fun getA(rok: Int) = when {
|
||||
rok <= 1582 -> 15
|
||||
rok <= 1699 -> 22
|
||||
rok <= 1899 -> 23
|
||||
rok <= 2199 -> 24
|
||||
rok <= 2299 -> 25
|
||||
rok <= 2399 -> 26
|
||||
rok <= 2499 -> 25
|
||||
else -> 0
|
||||
}
|
||||
|
||||
/* Pobierz wartosc B z tabeli lat */
|
||||
private fun getB(rok: Int) = when {
|
||||
rok <= 1582 -> 6
|
||||
rok <= 1699 -> 2
|
||||
rok <= 1799 -> 3
|
||||
rok <= 1899 -> 4
|
||||
rok <= 2099 -> 5
|
||||
rok <= 2199 -> 6
|
||||
rok <= 2299 -> 0
|
||||
rok <= 2499 -> 1
|
||||
else -> 0
|
||||
}
|
||||
|
||||
/* oblicz ile dni po 22 marca przypada wielkanoc */
|
||||
private fun Oblicz_Date_wielkanocy(rok: Int): Int {
|
||||
val a = rok % 19
|
||||
val b = rok % 4
|
||||
val c = rok % 7
|
||||
var d = (a * 19 + getA(rok)) % 30
|
||||
val e = (2 * b + 4 * c + 6 * d + getB(rok)) % 7
|
||||
if (d == 29 && e == 6 || d == 28 && e == 6) {
|
||||
d -= 7
|
||||
}
|
||||
return d + e
|
||||
}
|
||||
|
||||
private fun get_dataOf_bigNight(): Date {
|
||||
val date = Date.getToday()
|
||||
date.month = 4
|
||||
date.day = 22 + Oblicz_Date_wielkanocy(date.year)
|
||||
if (date.day > 31)
|
||||
date.day = date.day % 31
|
||||
else
|
||||
date.month = 3
|
||||
|
||||
return date
|
||||
}
|
||||
|
||||
fun isDataWielkanocyNearDzisiaj() =
|
||||
Date.diffDays(Date.getToday(), get_dataOf_bigNight()).absoluteValue < 7
|
||||
}
|
@ -1,7 +1,11 @@
|
||||
package pl.szczodrzynski.edziennik.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.view.menu.MenuBuilder
|
||||
import androidx.appcompat.view.menu.MenuPopupHelper
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
@ -29,10 +33,11 @@ open class TextInputDropDown : TextInputEditText {
|
||||
val selectedId
|
||||
get() = selected?.id
|
||||
|
||||
fun updateText() {
|
||||
private fun updateText() {
|
||||
setText(selected?.displayText ?: selected?.text)
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
open fun create(context: Context) {
|
||||
val drawable = IconicsDrawable(context, CommunityMaterial.Icon.cmd_chevron_down).apply {
|
||||
colorInt = Themes.getPrimaryTextColor(context)
|
||||
@ -58,7 +63,9 @@ open class TextInputDropDown : TextInputEditText {
|
||||
val popup = PopupMenu(context, this)
|
||||
|
||||
items.forEachIndexed { index, item ->
|
||||
popup.menu.add(0, item.id.toInt(), index, item.text)
|
||||
popup.menu.add(0, item.id.toInt(), index, item.text).also {
|
||||
it.icon = item.icon
|
||||
}
|
||||
}
|
||||
|
||||
popup.setOnMenuItemClickListener { menuItem ->
|
||||
@ -70,29 +77,46 @@ open class TextInputDropDown : TextInputEditText {
|
||||
true
|
||||
}
|
||||
|
||||
popup.setOnDismissListener {
|
||||
val helper = MenuPopupHelper(context, popup.menu as MenuBuilder, this)
|
||||
helper.setForceShowIcon(true)
|
||||
helper.setOnDismissListener {
|
||||
clearFocus()
|
||||
}
|
||||
|
||||
popup.show()
|
||||
helper.show()
|
||||
}
|
||||
}
|
||||
|
||||
fun select(item: Item): Item? {
|
||||
/**
|
||||
* Select an arbitrary [item]. Allows to select an item not present
|
||||
* in the original list.
|
||||
*/
|
||||
fun select(item: Item): Item {
|
||||
selected = item
|
||||
updateText()
|
||||
error = null
|
||||
return item
|
||||
}
|
||||
|
||||
/**
|
||||
* Select an item by its ID. Returns the selected item
|
||||
* if found.
|
||||
*/
|
||||
fun select(id: Long?): Item? {
|
||||
return items.singleOrNull { it.id == id }?.let { select(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Select an item by its tag. Returns the selected item
|
||||
* if found.
|
||||
*/
|
||||
fun select(tag: Any?): Item? {
|
||||
return items.singleOrNull { it.tag == tag }?.let { select(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Select an item by its index. Returns the selected item
|
||||
* if the index exists.
|
||||
*/
|
||||
fun select(index: Int): Item? {
|
||||
return items.getOrNull(index)?.let { select(it) }
|
||||
}
|
||||
@ -143,5 +167,11 @@ open class TextInputDropDown : TextInputEditText {
|
||||
}
|
||||
}
|
||||
|
||||
class Item(val id: Long, val text: CharSequence, val displayText: CharSequence? = null, val tag: Any? = null)
|
||||
class Item(
|
||||
val id: Long,
|
||||
val text: CharSequence,
|
||||
val displayText: CharSequence? = null,
|
||||
val tag: Any? = null,
|
||||
val icon: Drawable? = null
|
||||
)
|
||||
}
|
||||
|
@ -4,12 +4,17 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.utils.managers
|
||||
|
||||
import androidx.core.view.isVisible
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import com.mikepenz.iconics.view.IconicsTextView
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
import pl.szczodrzynski.edziennik.startCoroutineTimer
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class EventManager(val app: App) : CoroutineScope {
|
||||
@ -32,4 +37,41 @@ class EventManager(val app: App) : CoroutineScope {
|
||||
app.db.metadataDao().setSeen(event.profileId, event, true)
|
||||
}
|
||||
}
|
||||
|
||||
fun setEventTopic(
|
||||
title: IconicsTextView,
|
||||
event: EventFull,
|
||||
showType: Boolean = true,
|
||||
doneIconColor: Int? = null
|
||||
) {
|
||||
var eventTopic = if (showType)
|
||||
"${event.typeName ?: "wydarzenie"} - ${event.topic}"
|
||||
else
|
||||
event.topic
|
||||
|
||||
if (event.addedManually) {
|
||||
eventTopic = "{cmd-clipboard-edit-outline} $eventTopic"
|
||||
}
|
||||
|
||||
title.text = eventTopic
|
||||
|
||||
title.setCompoundDrawables(
|
||||
null,
|
||||
null,
|
||||
if (event.isDone) IconicsDrawable(title.context).apply {
|
||||
icon = CommunityMaterial.Icon.cmd_check
|
||||
colorInt = doneIconColor ?: R.color.md_green_500.resolveColor(title.context)
|
||||
sizeDp = 24
|
||||
} else null,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
fun setLegendText(legend: IconicsTextView, event: EventFull) {
|
||||
legend.text = listOfNotNull(
|
||||
if (event.addedManually) R.string.legend_event_added_manually else null,
|
||||
if (event.isDone) R.string.legend_event_is_done else null
|
||||
).map { legend.context.getString(it) }.join("\n")
|
||||
legend.isVisible = legend.text.isNotBlank()
|
||||
}
|
||||
}
|
||||
|
@ -94,17 +94,29 @@ public class Date implements Comparable<Date> {
|
||||
}
|
||||
}
|
||||
|
||||
public long getInMillis() {
|
||||
public Calendar getAsCalendar() {
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.set(year, month - 1, day, 0, 0, 0);
|
||||
c.set(Calendar.MILLISECOND, 0);
|
||||
return c;
|
||||
}
|
||||
|
||||
public Calendar getAsCalendar(Time time) {
|
||||
if (time == null)
|
||||
return getAsCalendar();
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.set(year, month - 1, day, time.hour, time.minute, time.second);
|
||||
c.set(Calendar.MILLISECOND, 0);
|
||||
return c;
|
||||
}
|
||||
|
||||
public long getInMillis() {
|
||||
Calendar c = getAsCalendar();
|
||||
return c.getTimeInMillis();
|
||||
}
|
||||
|
||||
public long getInMillisUtc() {
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.set(year, month - 1, day, 0, 0, 0);
|
||||
c.set(Calendar.MILLISECOND, 0);
|
||||
Calendar c = getAsCalendar();
|
||||
c.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
return c.getTimeInMillis();
|
||||
}
|
||||
@ -179,13 +191,7 @@ public class Date implements Comparable<Date> {
|
||||
}
|
||||
|
||||
public long combineWith(Time time) {
|
||||
if (time == null) {
|
||||
return getInMillis();
|
||||
}
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.set(this.year, this.month - 1, this.day, time.hour, time.minute, time.second);
|
||||
c.set(Calendar.MILLISECOND, 0);
|
||||
return c.getTimeInMillis();
|
||||
return getAsCalendar(time).getTimeInMillis();
|
||||
}
|
||||
|
||||
public int getWeekDay() {
|
||||
|
BIN
app/src/main/res/drawable/egg1.webp
Normal file
BIN
app/src/main/res/drawable/egg1.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
BIN
app/src/main/res/drawable/egg2.webp
Normal file
BIN
app/src/main/res/drawable/egg2.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.5 KiB |
BIN
app/src/main/res/drawable/egg3.webp
Normal file
BIN
app/src/main/res/drawable/egg3.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
BIN
app/src/main/res/drawable/egg4.webp
Normal file
BIN
app/src/main/res/drawable/egg4.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.1 KiB |
BIN
app/src/main/res/drawable/egg5.webp
Normal file
BIN
app/src/main/res/drawable/egg5.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.6 KiB |
BIN
app/src/main/res/drawable/egg6.webp
Normal file
BIN
app/src/main/res/drawable/egg6.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
15
app/src/main/res/drawable/timetable_marker_triangle.xml
Normal file
15
app/src/main/res/drawable/timetable_marker_triangle.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2021-4-14.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/md_red_500"
|
||||
android:pathData="M0,4V20L12,12.25" />
|
||||
</vector>
|
||||
|
61
app/src/main/res/layout/agenda_counter_item.xml
Normal file
61
app/src/main/res/layout/agenda_counter_item.xml
Normal file
@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2021-4-11.
|
||||
-->
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_rounded_8dp"
|
||||
android:foreground="@drawable/bg_rounded_8dp_outline"
|
||||
tools:backgroundTint="#ff1744"
|
||||
tools:foregroundTint="#ff1744">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="10dp"
|
||||
android:textAppearance="@style/NavView.TextView.Medium"
|
||||
tools:text="@string/agenda_lesson_changes"
|
||||
tools:textColor="@color/md_white_1000" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/count"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginVertical="2dp"
|
||||
android:gravity="center"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:textSize="20sp"
|
||||
tools:text="3"
|
||||
tools:textColor="@color/md_white_1000" />
|
||||
|
||||
<View
|
||||
android:id="@+id/badgeBackground"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:background="@drawable/bg_rounded_8dp"
|
||||
android:layout_marginRight="-24dp"
|
||||
android:layout_marginEnd="-24dp"
|
||||
android:layout_marginTop="-24dp"
|
||||
tools:backgroundTint="?android:colorBackground"/>
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/badge"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_gravity="end"
|
||||
android:layout_margin="8dp"
|
||||
android:background="@drawable/unread_red_circle" />
|
||||
</FrameLayout>
|
57
app/src/main/res/layout/agenda_event_compact_item.xml
Normal file
57
app/src/main/res/layout/agenda_event_compact_item.xml
Normal file
@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2021-4-8.
|
||||
-->
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_rounded_8dp"
|
||||
android:foreground="@drawable/bg_rounded_8dp_outline"
|
||||
tools:backgroundTint="@color/blue_selected"
|
||||
tools:foregroundTint="@color/blue_selected">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp">
|
||||
|
||||
<com.mikepenz.iconics.view.IconicsTextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textSize="16sp"
|
||||
tools:text="sprawdzian - Język polski"
|
||||
tools:textColor="@color/md_white_1000" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/badgeBackground"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="-24dp"
|
||||
android:layout_marginEnd="-24dp"
|
||||
android:layout_marginRight="-24dp"
|
||||
android:background="@drawable/bg_rounded_8dp"
|
||||
tools:backgroundTint="?android:colorBackground" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/badge"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_gravity="end"
|
||||
android:layout_margin="8dp"
|
||||
android:background="@drawable/unread_red_circle" />
|
||||
</FrameLayout>
|
66
app/src/main/res/layout/agenda_event_item.xml
Normal file
66
app/src/main/res/layout/agenda_event_item.xml
Normal file
@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2021-4-8.
|
||||
-->
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_rounded_8dp"
|
||||
android:foreground="@drawable/bg_rounded_8dp_outline"
|
||||
tools:backgroundTint="@color/blue_selected"
|
||||
tools:foregroundTint="@color/blue_selected">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp">
|
||||
|
||||
<com.mikepenz.iconics.view.IconicsTextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="3"
|
||||
android:textSize="16sp"
|
||||
tools:text="sprawdzian - Język polski"
|
||||
tools:textColor="@color/md_white_1000" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:textSize="12sp"
|
||||
tools:text="9:05, biologia, Jan Kowalski, 7a"
|
||||
tools:textColor="@color/md_white_1000" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/badgeBackground"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="-24dp"
|
||||
android:layout_marginEnd="-24dp"
|
||||
android:layout_marginRight="-24dp"
|
||||
android:background="@drawable/bg_rounded_8dp"
|
||||
tools:backgroundTint="?android:colorBackground" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/badge"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_gravity="end"
|
||||
android:layout_margin="8dp"
|
||||
android:background="@drawable/unread_red_circle" />
|
||||
</FrameLayout>
|
58
app/src/main/res/layout/agenda_group_item.xml
Normal file
58
app/src/main/res/layout/agenda_group_item.xml
Normal file
@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2021-4-10.
|
||||
-->
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_rounded_8dp"
|
||||
android:foreground="@drawable/bg_rounded_8dp_outline"
|
||||
tools:backgroundTint="@color/blue_selected"
|
||||
tools:foregroundTint="@color/blue_selected">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
android:padding="10dp"
|
||||
android:textAppearance="@style/NavView.TextView.Medium"
|
||||
tools:text="informacja"
|
||||
tools:textColor="@color/md_white_1000" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/count"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginVertical="2dp"
|
||||
android:layout_marginRight="-1dp"
|
||||
android:background="@drawable/bg_rounded_8dp"
|
||||
android:gravity="center"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingEnd="18dp"
|
||||
android:paddingRight="18dp"
|
||||
android:textSize="20sp"
|
||||
tools:backgroundTint="?android:colorBackground"
|
||||
tools:text="3" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/badge"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_gravity="end"
|
||||
android:layout_margin="6dp"
|
||||
android:background="@drawable/unread_red_circle" />
|
||||
</FrameLayout>
|
@ -1,15 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2021-4-11.
|
||||
-->
|
||||
|
||||
<com.github.tibolte.agendacalendarview.agenda.AgendaEventView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
|
||||
<include
|
||||
layout="@layout/row_lesson_change_item"
|
||||
android:id="@+id/item"
|
||||
layout="@layout/agenda_counter_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
@ -1,13 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2021-4-9.
|
||||
-->
|
||||
|
||||
<com.github.tibolte.agendacalendarview.agenda.AgendaEventView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
|
||||
<include
|
||||
layout="@layout/row_teacher_absence_item"
|
||||
android:id="@+id/item"
|
||||
layout="@layout/agenda_event_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
18
app/src/main/res/layout/agenda_wrapped_event_compact.xml
Normal file
18
app/src/main/res/layout/agenda_wrapped_event_compact.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2021-4-11.
|
||||
-->
|
||||
|
||||
<com.github.tibolte.agendacalendarview.agenda.AgendaEventView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<include
|
||||
android:id="@+id/item"
|
||||
layout="@layout/agenda_event_compact_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
</com.github.tibolte.agendacalendarview.agenda.AgendaEventView>
|
18
app/src/main/res/layout/agenda_wrapped_group.xml
Normal file
18
app/src/main/res/layout/agenda_wrapped_group.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2021-4-10.
|
||||
-->
|
||||
|
||||
<com.github.tibolte.agendacalendarview.agenda.AgendaEventView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<include
|
||||
android:id="@+id/item"
|
||||
layout="@layout/agenda_group_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
</com.github.tibolte.agendacalendarview.agenda.AgendaEventView>
|
@ -94,6 +94,7 @@
|
||||
android:textAppearance="@style/NavView.TextView.Helper" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/teacherName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="0dp"
|
||||
|
156
app/src/main/res/layout/dialog_config_agenda.xml
Normal file
156
app/src/main/res/layout/dialog_config_agenda.xml
Normal file
@ -0,0 +1,156 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2021-4-10.
|
||||
-->
|
||||
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
<import type="android.view.View" />
|
||||
|
||||
<variable
|
||||
name="config"
|
||||
type="pl.szczodrzynski.edziennik.config.ProfileConfigUI" />
|
||||
|
||||
<variable
|
||||
name="isAgendaMode"
|
||||
type="boolean" />
|
||||
</data>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/agenda_config_appearance"
|
||||
android:textAppearance="@style/NavView.TextView.Subtitle" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:checked="@={config.agendaLessonChanges}"
|
||||
android:minHeight="32dp"
|
||||
android:text="@string/agenda_config_lesson_changes" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:checked="@={config.agendaTeacherAbsence}"
|
||||
android:minHeight="32dp"
|
||||
android:text="@string/agenda_config_teacher_absence" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:checked="@={config.agendaCompactMode}"
|
||||
android:enabled="@{isAgendaMode}"
|
||||
android:minHeight="32dp"
|
||||
android:text="@string/agenda_config_compact_mode"
|
||||
tools:enabled="false" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:enabled="@{isAgendaMode}"
|
||||
android:text="@string/agenda_config_compact_mode_hint"
|
||||
android:textAppearance="@style/NavView.TextView.Helper"
|
||||
tools:enabled="false" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:checked="@={config.agendaGroupByType}"
|
||||
android:enabled="@{isAgendaMode}"
|
||||
android:minHeight="32dp"
|
||||
android:text="@string/agenda_config_group_by_type"
|
||||
tools:enabled="false" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/agenda_config_mode_unavailable"
|
||||
android:textStyle="italic"
|
||||
android:visibility="@{isAgendaMode ? View.GONE : View.VISIBLE}" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/agenda_config_event_sharing"
|
||||
android:textAppearance="@style/NavView.TextView.Subtitle" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/eventSharingEnabled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:minHeight="32dp"
|
||||
android:text="@string/agenda_config_event_sharing_enabled" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/agenda_config_elearning"
|
||||
android:textAppearance="@style/NavView.TextView.Subtitle" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/agenda_config_not_available_yet"
|
||||
android:textAppearance="@style/NavView.TextView.Small"
|
||||
android:textStyle="italic" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/elearningEnabled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:checked="@={config.agendaElearningMark}"
|
||||
android:enabled="false"
|
||||
android:minHeight="32dp"
|
||||
android:onClick="@{() -> elearningType.setEnabled(elearningEnabled.isChecked())}"
|
||||
android:text="@string/agenda_config_elearning_mark" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/elearningType"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:enabled="@{config.agendaElearningMark}"
|
||||
android:hint="@string/agenda_config_elearning_type">
|
||||
|
||||
<pl.szczodrzynski.edziennik.utils.TextInputDropDown
|
||||
android:id="@+id/elearningTypeDropdown"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:enabled="false"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:checked="@={config.agendaElearningGroup}"
|
||||
android:enabled="false"
|
||||
android:minHeight="32dp"
|
||||
android:text="@string/agenda_config_elearning_group" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</layout>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user