mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-06-24 11:05:48 +02:00
Compare commits
24 Commits
v4.0-beta.
...
v4.0-beta.
Author | SHA1 | Date | |
---|---|---|---|
4aa31424d6 | |||
8a825227cb | |||
cc1b581d7e | |||
9936d90ae2 | |||
df1a241b2b | |||
ae89b33fb7 | |||
e05b483f5c | |||
715f536b23 | |||
930813fb8a | |||
acd5e9b998 | |||
06011bf4ae | |||
30e15b813c | |||
fcd7a7f349 | |||
42ef40439e | |||
098beb14fe | |||
0b186a754a | |||
d00963b53d | |||
e282af0e80 | |||
630361849c | |||
88a1de50ca | |||
d8263d0b6a | |||
611ab0f100 | |||
70c307b796 | |||
054a233ad6 |
@ -1,19 +1,23 @@
|
||||
<h3>Wersja 4.0-beta.11, 2020-03-06</h3>
|
||||
<h3>Wersja 4.0-beta.12, 2020-03-10</h3>
|
||||
<ul>
|
||||
<li><b>Przebudowaliśmy cały moduł synchronizacji</b>, co oznacza większą stabilność aplikacji, szybkosć oraz poprawność pobieranych danych.</li>
|
||||
<li><b>Przebudowaliśmy cały moduł synchronizacji</b>, co oznacza większą stabilność aplikacji, szybkość oraz poprawność pobieranych danych.</li>
|
||||
<li><b><u>Wysyłanie wiadomości</u></b> - funkcja, na którą czekał każdy. Od teraz w Szkolnym można wysyłać oraz odpowiadać na wiadomości do nauczycieli 👏</li>
|
||||
<li>Udoskonalony wygląd Szkolnego - sprawi, że korzystanie z aplikacji będzie jeszcze przyjemniejsze</li>
|
||||
<li>Nowa <b>Strona główna</b> - ładniejszy wygląd oraz możliwość przestawiania kart na każdym profilu</li>
|
||||
<li>Nowy <b>Plan lekcji</b> - z doskonałą obsługą lekcji przesuniętych oraz dwóch lekcji o tej samej godzinie</li>
|
||||
<li>Nowe <b>Oceny</b> - z możliwością zmiany wartości plusów oraz minusów oraz wyłączenia niektórych ocen ze średniej</li>
|
||||
<li>Opcja wyłączenia wybranych powiadomień z aplikacji</li>
|
||||
<br>
|
||||
<br>
|
||||
<li>Nowe okienka informacji o wydarzeniach oraz lekcjach</li>
|
||||
<li>Nowe, przyjemniejsze powiadomienia</li>
|
||||
<li>Łatwiejsze dodawanie własnych wydarzeń</li>
|
||||
<li>Dużo poprawek w widoku <b>Wiadomości</b> oraz <b>Ogłoszeń</b></li>
|
||||
<li>Częściowa <b>Obsługa dziennika EduDziennik</b></li>
|
||||
<li>Librus: opcja logowania w dziennikach <b>Jednostek Samorządu Terytorialnego</b> oraz <b>Oświata w Radomiu</b></li>
|
||||
<li>Librus: <b>poprawione obliczanie frekwencji</b></li>
|
||||
<li>Librus: obsługa Zadań domowych bez posiadania Mobilnych dodatków (przez system Synergia)</li>
|
||||
<li>Lepsze <b>przekazywanie powiadomień na komputer</b> oraz łatwiejsze parowanie</li>
|
||||
<li>Łatwiejsze dodawanie własnych wydarzeń</li>
|
||||
<li>Poprawiliśmy synchronizację w tle na niektórych telefonach</li>
|
||||
<li>Usunąłem denerwujący brak zaznaczenia w lewym menu</li>
|
||||
<li>Znaczna ilość błędów z poprzednich wersji już nie występuje</li>
|
||||
@ -26,7 +30,6 @@
|
||||
Staramy się usuwać takie przypadki, jednak na chwilę obecną mogą występować błędy w:
|
||||
<ul>
|
||||
<li>Wysyłanie wiadomości może nie działać w pełni prawidłowo - proszę o zgłaszanie wszystkich błędów na naszym serwerze Discord</li>
|
||||
<li>Cisza nocna w powiadomieniach jeszcze nie działa.</li>
|
||||
</ul>
|
||||
<br>
|
||||
<br>
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
/*secret password - removed for source code publication*/
|
||||
static toys AES_IV[16] = {
|
||||
0x00, 0x37, 0x6a, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
0xb8, 0x59, 0x75, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
|
||||
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);
|
||||
|
||||
|
@ -47,6 +47,7 @@ import pl.szczodrzynski.edziennik.ui.modules.base.CrashActivity
|
||||
import pl.szczodrzynski.edziennik.utils.*
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager
|
||||
import pl.szczodrzynski.edziennik.utils.managers.NotificationChannelsManager
|
||||
import pl.szczodrzynski.edziennik.utils.managers.TimetableManager
|
||||
import pl.szczodrzynski.edziennik.utils.managers.UserActionManager
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
@ -67,6 +68,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
|
||||
val notificationChannelsManager by lazy { NotificationChannelsManager(this) }
|
||||
val userActionManager by lazy { UserActionManager(this) }
|
||||
val gradesManager by lazy { GradesManager(this) }
|
||||
val timetableManager by lazy { TimetableManager(this) }
|
||||
|
||||
val db
|
||||
get() = App.db
|
||||
|
@ -12,7 +12,6 @@ import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
@ -42,7 +41,6 @@ import pl.droidsonroids.gif.GifDrawable
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.api.events.*
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata.*
|
||||
import pl.szczodrzynski.edziennik.databinding.ActivitySzkolnyBinding
|
||||
@ -286,8 +284,6 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
||||
|
||||
setContentView(b.root)
|
||||
|
||||
Log.d(TAG, Signing.appPassword)
|
||||
|
||||
mainSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar)
|
||||
errorSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar)
|
||||
|
||||
@ -906,7 +902,6 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
||||
bottomSheet.close()
|
||||
bottomSheet.removeAllContextual()
|
||||
bottomSheet.toggleGroupEnabled = false
|
||||
bottomSheet.onCloseListener = null
|
||||
drawer.close()
|
||||
drawer.setSelection(target.id, fireOnClick = false)
|
||||
navView.toolbar.setTitle(target.title ?: target.name)
|
||||
|
@ -22,7 +22,7 @@ import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
|
||||
companion object {
|
||||
const val DATA_VERSION = 10
|
||||
const val DATA_VERSION = 11
|
||||
}
|
||||
|
||||
private val job = Job()
|
||||
|
@ -7,6 +7,7 @@ package pl.szczodrzynski.edziennik.config
|
||||
import pl.szczodrzynski.edziennik.config.utils.get
|
||||
import pl.szczodrzynski.edziennik.config.utils.getIntList
|
||||
import pl.szczodrzynski.edziennik.config.utils.set
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
class ConfigSync(private val config: Config) {
|
||||
private var mDontShowAppManagerDialog: Boolean? = null
|
||||
@ -40,14 +41,19 @@ class ConfigSync(private val config: Config) {
|
||||
| | | | | | | |/ _ \ __| | '_ \ / _ \| | | | '__/ __|
|
||||
| |__| | |_| | | __/ |_ | | | | (_) | |_| | | \__ \
|
||||
\___\_\\__,_|_|\___|\__| |_| |_|\___/ \__,_|_| |__*/
|
||||
private var mQuietHoursStart: Long? = null
|
||||
var quietHoursStart: Long
|
||||
get() { mQuietHoursStart = mQuietHoursStart ?: config.values.get("quietHoursStart", 0L); return mQuietHoursStart ?: 0L }
|
||||
private var mQuietHoursEnabled: Boolean? = null
|
||||
var quietHoursEnabled: Boolean
|
||||
get() { mQuietHoursEnabled = mQuietHoursEnabled ?: config.values.get("quietHoursEnabled", false); return mQuietHoursEnabled ?: false }
|
||||
set(value) { config.set("quietHoursEnabled", value); mQuietHoursEnabled = value }
|
||||
|
||||
private var mQuietHoursStart: Time? = null
|
||||
var quietHoursStart: Time?
|
||||
get() { mQuietHoursStart = mQuietHoursStart ?: config.values.get("quietHoursStart", null as Time?); return mQuietHoursStart }
|
||||
set(value) { config.set("quietHoursStart", value); mQuietHoursStart = value }
|
||||
|
||||
private var mQuietHoursEnd: Long? = null
|
||||
var quietHoursEnd: Long
|
||||
get() { mQuietHoursEnd = mQuietHoursEnd ?: config.values.get("quietHoursEnd", 0L); return mQuietHoursEnd ?: 0L }
|
||||
private var mQuietHoursEnd: Time? = null
|
||||
var quietHoursEnd: Time?
|
||||
get() { mQuietHoursEnd = mQuietHoursEnd ?: config.values.get("quietHoursEnd", null as Time?); return mQuietHoursEnd }
|
||||
set(value) { config.set("quietHoursEnd", value); mQuietHoursEnd = value }
|
||||
|
||||
private var mQuietDuringLessons: Boolean? = null
|
||||
|
@ -26,6 +26,16 @@ class ProfileConfigGrades(private val config: ProfileConfig) {
|
||||
get() { mCountZeroToAvg = mCountZeroToAvg ?: config.values.get("countZeroToAvg", true); return mCountZeroToAvg ?: true }
|
||||
set(value) { config.set("countZeroToAvg", value); mCountZeroToAvg = value }
|
||||
|
||||
private var mHideImproved: Boolean? = null
|
||||
var hideImproved: Boolean
|
||||
get() { mHideImproved = mHideImproved ?: config.values.get("hideImproved", false); return mHideImproved ?: false }
|
||||
set(value) { config.set("hideImproved", value); mHideImproved = value }
|
||||
|
||||
private var mAverageWithoutWeight: Boolean? = null
|
||||
var averageWithoutWeight: Boolean
|
||||
get() { mAverageWithoutWeight = mAverageWithoutWeight ?: config.values.get("averageWithoutWeight", true); return mAverageWithoutWeight ?: true }
|
||||
set(value) { config.set("averageWithoutWeight", value); mAverageWithoutWeight = value }
|
||||
|
||||
private var mPlusValue: Float? = null
|
||||
var plusValue: Float?
|
||||
get() { mPlusValue = mPlusValue ?: config.values.getFloat("plusValue"); return mPlusValue }
|
||||
|
@ -14,6 +14,7 @@ import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_LIBRUS
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_MOBIDZIENNIK
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_VULCAN
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import kotlin.math.abs
|
||||
|
||||
class AppConfigMigrationV3(p: SharedPreferences, config: Config) {
|
||||
init { config.apply {
|
||||
@ -42,9 +43,7 @@ class AppConfigMigrationV3(p: SharedPreferences, config: Config) {
|
||||
devModePassword = p.getString("$s.devModePassword", null).fix()
|
||||
sync.tokenApp = p.getString("$s.fcmToken", null).fix()
|
||||
timetable.bellSyncMultiplier = p.getString("$s.bellSyncMultiplier", null)?.toIntOrNull() ?: 0
|
||||
sync.quietHoursStart = p.getString("$s.quietHoursStart", null)?.toLongOrNull() ?: 0
|
||||
appRateSnackbarTime = p.getString("$s.appRateSnackbarTime", null)?.toLongOrNull() ?: 0
|
||||
sync.quietHoursEnd = p.getString("$s.quietHoursEnd", null)?.toLongOrNull() ?: 0
|
||||
timetable.countInSeconds = p.getString("$s.countInSeconds", null)?.toBoolean() ?: false
|
||||
ui.headerBackground = p.getString("$s.headerBackground", null).fix()
|
||||
ui.appBackground = p.getString("$s.appBackground", null).fix()
|
||||
@ -59,6 +58,22 @@ class AppConfigMigrationV3(p: SharedPreferences, config: Config) {
|
||||
sync.notifyAboutUpdates = p.getString("$s.notifyAboutUpdates", null)?.toBoolean() ?: true
|
||||
timetable.bellSyncDiff = p.getString("$s.bellSyncDiff", null)?.let { Gson().fromJson(it, Time::class.java) }
|
||||
|
||||
val startMillis = p.getString("$s.quietHoursStart", null)?.toLongOrNull() ?: 0
|
||||
val endMillis = p.getString("$s.quietHoursEnd", null)?.toLongOrNull() ?: 0
|
||||
if (startMillis > 0) {
|
||||
try {
|
||||
sync.quietHoursStart = Time.fromMillis(abs(startMillis))
|
||||
sync.quietHoursEnd = Time.fromMillis(abs(endMillis))
|
||||
sync.quietHoursEnabled = true
|
||||
}
|
||||
catch (_: Exception) {}
|
||||
}
|
||||
else {
|
||||
sync.quietHoursEnabled = false
|
||||
sync.quietHoursStart = null
|
||||
sync.quietHoursEnd = null
|
||||
}
|
||||
|
||||
sync.tokenMobidziennikList = listOf()
|
||||
sync.tokenVulcanList = listOf()
|
||||
sync.tokenLibrusList = listOf()
|
||||
|
@ -11,6 +11,8 @@ import pl.szczodrzynski.edziennik.HOUR
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.config.Config
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.ORDER_BY_DATE_DESC
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import kotlin.math.abs
|
||||
|
||||
class ConfigMigration(app: App, config: Config) {
|
||||
init { config.apply {
|
||||
@ -43,8 +45,9 @@ class ConfigMigration(app: App, config: Config) {
|
||||
sync.interval = 1*HOUR.toInt()
|
||||
sync.notifyAboutUpdates = true
|
||||
sync.onlyWifi = false
|
||||
sync.quietHoursStart = 0
|
||||
sync.quietHoursEnd = 0
|
||||
sync.quietHoursEnabled = false
|
||||
sync.quietHoursStart = null
|
||||
sync.quietHoursEnd = null
|
||||
sync.quietDuringLessons = false
|
||||
sync.tokenApp = null
|
||||
sync.tokenMobidziennik = null
|
||||
@ -69,5 +72,25 @@ class ConfigMigration(app: App, config: Config) {
|
||||
|
||||
dataVersion = 10
|
||||
}
|
||||
|
||||
if (dataVersion < 11) {
|
||||
val startMillis = config.values.get("quietHoursStart", 0L)
|
||||
val endMillis = config.values.get("quietHoursEnd", 0L)
|
||||
if (startMillis > 0) {
|
||||
try {
|
||||
sync.quietHoursStart = Time.fromMillis(abs(startMillis))
|
||||
sync.quietHoursEnd = Time.fromMillis(abs(endMillis))
|
||||
sync.quietHoursEnabled = true
|
||||
}
|
||||
catch (_: Exception) {}
|
||||
}
|
||||
else {
|
||||
sync.quietHoursEnabled = false
|
||||
sync.quietHoursStart = null
|
||||
sync.quietHoursEnd = null
|
||||
}
|
||||
|
||||
dataVersion = 11
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ open class LibrusApi(open val data: DataLibrus, open val lastSync: Long?) {
|
||||
val error = if (response?.code() == 200) null else
|
||||
json.getString("Code") ?:
|
||||
json.getString("Message") ?:
|
||||
json.getString("Status") ?:
|
||||
response?.parserErrorBody
|
||||
error?.let { code ->
|
||||
when (code) {
|
||||
@ -67,6 +68,7 @@ open class LibrusApi(open val data: DataLibrus, open val lastSync: Long?) {
|
||||
"Nieprawidłowy węzeł." -> ERROR_LIBRUS_API_INCORRECT_ENDPOINT
|
||||
"NoticeboardProblem" -> ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM
|
||||
"DeviceRegistered" -> ERROR_LIBRUS_API_DEVICE_REGISTERED
|
||||
"Maintenance" -> ERROR_LIBRUS_API_MAINTENANCE
|
||||
else -> ERROR_LIBRUS_API_OTHER
|
||||
}.let { errorCode ->
|
||||
if (errorCode !in ignoreErrors) {
|
||||
@ -119,6 +121,7 @@ open class LibrusApi(open val data: DataLibrus, open val lastSync: Long?) {
|
||||
.allowErrorCode(HTTP_UNAUTHORIZED)
|
||||
.allowErrorCode(HTTP_UNAVAILABLE)
|
||||
.allowErrorCode(HTTP_NOT_FOUND)
|
||||
.allowErrorCode(503)
|
||||
.callback(callback)
|
||||
.build()
|
||||
.enqueue()
|
||||
|
@ -44,6 +44,7 @@ open class LibrusPortal(open val data: DataLibrus) {
|
||||
"Access token is invalid" -> ERROR_LIBRUS_PORTAL_ACCESS_DENIED
|
||||
"ApiDisabled" -> ERROR_LIBRUS_PORTAL_API_DISABLED
|
||||
"Account not found" -> ERROR_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND
|
||||
"Unable to refresh the account" -> ERROR_LIBRUS_PORTAL_MAINTENANCE
|
||||
else -> when (json.getString("hint")) {
|
||||
"Error while decoding to JSON" -> ERROR_LIBRUS_PORTAL_ACCESS_DENIED
|
||||
else -> ERROR_LIBRUS_PORTAL_OTHER
|
||||
@ -97,6 +98,7 @@ open class LibrusPortal(open val data: DataLibrus) {
|
||||
.allowErrorCode(HttpURLConnection.HTTP_UNAUTHORIZED)
|
||||
.allowErrorCode(HttpURLConnection.HTTP_BAD_REQUEST)
|
||||
.allowErrorCode(HttpURLConnection.HTTP_GONE)
|
||||
.allowErrorCode(424)
|
||||
.callback(callback)
|
||||
.build()
|
||||
.enqueue()
|
||||
|
@ -27,6 +27,17 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
|
||||
|
||||
private val nameFormat by lazy { DecimalFormat("#.##") }
|
||||
|
||||
private val types by lazy {
|
||||
mapOf(
|
||||
1 to ("wz" to "wzorowe"),
|
||||
2 to ("bdb" to "bardzo dobre"),
|
||||
3 to ("db" to "dobre"),
|
||||
4 to ("popr" to "poprawne"),
|
||||
5 to ("ndp" to "nieodpowiednie"),
|
||||
6 to ("ng" to "naganne")
|
||||
)
|
||||
}
|
||||
|
||||
init { data.profile?.also { profile ->
|
||||
apiGet(TAG, "BehaviourGrades/Points") { json ->
|
||||
|
||||
@ -95,8 +106,12 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
|
||||
val addedDate = grade.getString("AddDate")?.let { Date.fromIso(it) }
|
||||
?: System.currentTimeMillis()
|
||||
|
||||
val text = grade.getString("Text")
|
||||
val type = grade.getJsonObject("BehaviourGrade")?.getInt("Id")?.let { types[it] }
|
||||
|
||||
val name = when {
|
||||
value != null -> (if (value >= 0) "+" else "") + nameFormat.format(value)
|
||||
type != null -> type.first
|
||||
value != null -> (if (value > 0) "+" else "") + nameFormat.format(value)
|
||||
shortName != null -> shortName
|
||||
else -> return@forEach
|
||||
}
|
||||
@ -115,14 +130,14 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
|
||||
|
||||
val categoryName = category?.text ?: ""
|
||||
|
||||
val description = grade.getJsonArray("Comments")?.asJsonObjectList()?.let { comments ->
|
||||
if (comments.isNotEmpty()) {
|
||||
data.gradeCategories.singleOrNull {
|
||||
it.type == GradeCategory.TYPE_BEHAVIOUR_COMMENT
|
||||
&& it.categoryId == comments[0].asJsonObject.getLong("Id")
|
||||
}?.text
|
||||
} else null
|
||||
} ?: ""
|
||||
val comments = grade.getJsonArray("Comments")
|
||||
?.asJsonObjectList()
|
||||
?.mapNotNull { comment ->
|
||||
val cId = comment.getLong("Id") ?: return@mapNotNull null
|
||||
data.gradeCategories[cId]?.text
|
||||
} ?: listOf()
|
||||
|
||||
val description = listOfNotNull(type?.second) + comments
|
||||
|
||||
val valueFrom = value ?: category?.valueFrom ?: 0f
|
||||
val valueTo = category?.valueTo ?: 0f
|
||||
@ -136,8 +151,8 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
|
||||
weight = -1f,
|
||||
color = color,
|
||||
category = categoryName,
|
||||
description = description,
|
||||
comment = null,
|
||||
description = text ?: description.join(" - "),
|
||||
comment = if (text != null) description.join(" - ") else null,
|
||||
semester = semester,
|
||||
teacherId = teacherId,
|
||||
subjectId = 1
|
||||
|
@ -57,7 +57,7 @@ class LibrusApiTextGrades(override val data: DataLibrus,
|
||||
color = category?.color ?: -1,
|
||||
category = category?.text ?: "",
|
||||
description = description,
|
||||
comment = null,
|
||||
comment = grade.getString("Phrase") /* whatever it is */,
|
||||
semester = semester,
|
||||
teacherId = teacherId,
|
||||
subjectId = subjectId
|
||||
|
@ -46,6 +46,6 @@ object Signing {
|
||||
|
||||
/*fun provideKey(param1: String, param2: Long): ByteArray {*/
|
||||
fun pleaseStopRightNow(param1: String, param2: Long): ByteArray {
|
||||
return "$param1.MTIzNDU2Nzg5MDAtDsP5Si===.$param2".sha256()
|
||||
return "$param1.MTIzNDU2Nzg5MDwj/ezwig===.$param2".sha256()
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import androidx.core.util.forEach
|
||||
import androidx.core.util.set
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_SERVER_MESSAGE
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification as AppNotification
|
||||
|
||||
class PostNotifications(val app: App, nList: List<AppNotification>) {
|
||||
@ -17,22 +18,34 @@ class PostNotifications(val app: App, nList: List<AppNotification>) {
|
||||
private const val TAG = "PostNotifications"
|
||||
}
|
||||
|
||||
/*public boolean shouldBeQuiet() {
|
||||
long now = Time.getNow().getInMillis();
|
||||
long start = app.config.getSync().getQuietHoursStart();
|
||||
long end = app.config.getSync().getQuietHoursEnd();
|
||||
private val quiet by lazy { shouldBeQuiet() }
|
||||
fun shouldBeQuiet(): Boolean {
|
||||
if (!app.config.sync.quietHoursEnabled)
|
||||
return false
|
||||
val now = Time.getNow().value
|
||||
val start = app.config.sync.quietHoursStart?.value ?: return false
|
||||
var end = app.config.sync.quietHoursEnd?.value ?: return false
|
||||
if (start > end) {
|
||||
end += 1000 * 60 * 60 * 24;
|
||||
//Log.d(TAG, "Night passing");
|
||||
// the range spans between two days
|
||||
end += 240000
|
||||
}
|
||||
if (start > now) {
|
||||
now += 1000 * 60 * 60 * 24;
|
||||
//Log.d(TAG, "Now is smaller");
|
||||
return now in start..end || now+240000 in start..end
|
||||
}
|
||||
|
||||
private fun NotificationCompat.Builder.addDefaults(): NotificationCompat.Builder {
|
||||
return this.setColor(0xff2196f3.toInt())
|
||||
.setLights(0xff2196f3.toInt(), 2000, 2000)
|
||||
.setPriority(if (quiet) NotificationCompat.PRIORITY_LOW else NotificationCompat.PRIORITY_MAX)
|
||||
.also {
|
||||
if (quiet) {
|
||||
it.setSound(null)
|
||||
it.setVibrate(longArrayOf())
|
||||
}
|
||||
else
|
||||
it.setDefaults(NotificationCompat.DEFAULT_ALL)
|
||||
}
|
||||
.setGroup(if (quiet) app.notificationChannelsManager.dataQuiet.key else app.notificationChannelsManager.data.key)
|
||||
}
|
||||
//Log.d(TAG, "Start is "+start+", now is "+now+", end is "+end);
|
||||
return start > 0 && now >= start && now <= end;
|
||||
}*/
|
||||
fun shouldBeQuiet() = false
|
||||
|
||||
private fun buildSummaryText(summaryCounts: SparseIntArray): CharSequence {
|
||||
val summaryTexts = mutableListOf<String>()
|
||||
@ -108,11 +121,7 @@ class PostNotifications(val app: App, nList: List<AppNotification>) {
|
||||
it.addLine(line)
|
||||
}
|
||||
})
|
||||
.setColor(0xff2196f3.toInt())
|
||||
.setLights(0xff2196f3.toInt(), 2000, 2000)
|
||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
||||
.setGroup(app.notificationChannelsManager.data.key)
|
||||
.addDefaults()
|
||||
.setContentIntent(summaryIntent)
|
||||
.setAutoCancel(true)
|
||||
.build()
|
||||
@ -131,11 +140,7 @@ class PostNotifications(val app: App, nList: List<AppNotification>) {
|
||||
.setStyle(NotificationCompat.BigTextStyle()
|
||||
.bigText(it.text))
|
||||
.setWhen(it.addedDate)
|
||||
.setColor(0xff2196f3.toInt())
|
||||
.setLights(0xff2196f3.toInt(), 2000, 2000)
|
||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
||||
.setGroup(app.notificationChannelsManager.data.key)
|
||||
.addDefaults()
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||
.setContentIntent(it.getPendingIntent(app))
|
||||
.setAutoCancel(true)
|
||||
@ -155,11 +160,7 @@ class PostNotifications(val app: App, nList: List<AppNotification>) {
|
||||
.setContentText(buildSummaryText(summaryCounts))
|
||||
.setTicker(newNotificationsText)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setColor(0xff2196f3.toInt())
|
||||
.setLights(0xff2196f3.toInt(), 2000, 2000)
|
||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
||||
.setGroup(app.notificationChannelsManager.data.key)
|
||||
.addDefaults()
|
||||
.setGroupSummary(true)
|
||||
.setContentIntent(summaryIntent)
|
||||
.setAutoCancel(true)
|
||||
|
@ -41,8 +41,9 @@ import pl.szczodrzynski.edziennik.data.db.migration.*
|
||||
Lesson::class,
|
||||
ConfigEntry::class,
|
||||
LibrusLesson::class,
|
||||
TimetableManual::class,
|
||||
Metadata::class
|
||||
], version = 78)
|
||||
], version = 79)
|
||||
@TypeConverters(
|
||||
ConverterTime::class,
|
||||
ConverterDate::class,
|
||||
@ -80,6 +81,7 @@ abstract class AppDb : RoomDatabase() {
|
||||
abstract fun timetableDao(): TimetableDao
|
||||
abstract fun configDao(): ConfigDao
|
||||
abstract fun librusLessonDao(): LibrusLessonDao
|
||||
abstract fun timetableManualDao(): TimetableManualDao
|
||||
abstract fun metadataDao(): MetadataDao
|
||||
|
||||
companion object {
|
||||
@ -161,7 +163,8 @@ abstract class AppDb : RoomDatabase() {
|
||||
Migration75(),
|
||||
Migration76(),
|
||||
Migration77(),
|
||||
Migration78()
|
||||
Migration78(),
|
||||
Migration79()
|
||||
).allowMainThreadQueries().build()
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class ConverterDateInt {
|
||||
@TypeConverter
|
||||
fun toDate(value: Int): Date = Date.fromValue(value)
|
||||
fun toDate(value: Int): Date? = if (value == 0) null else Date.fromValue(value)
|
||||
|
||||
@TypeConverter
|
||||
fun toInt(date: Date?): Int = date?.value ?: 0
|
||||
|
@ -5,10 +5,9 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.dao
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.*
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||
import androidx.sqlite.db.SupportSQLiteQuery
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
|
||||
@ -53,6 +52,9 @@ interface TimetableDao {
|
||||
@Query("DELETE FROM timetable WHERE profileId = :profileId AND type != -1 AND ((type != 3 AND date >= :dateFrom AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom AND oldDate <= :dateTo))")
|
||||
fun clearBetweenDates(profileId: Int, dateFrom: Date, dateTo: Date)
|
||||
|
||||
@RawQuery(observedEntities = [Lesson::class])
|
||||
fun getRaw(query: SupportSQLiteQuery): LiveData<List<LessonFull>>
|
||||
|
||||
@Query("""
|
||||
$QUERY
|
||||
WHERE timetable.profileId = :profileId AND type != -1 AND type != 0
|
||||
@ -67,12 +69,11 @@ interface TimetableDao {
|
||||
""")
|
||||
fun getChangesForDateNow(profileId: Int, date: Date): List<LessonFull>
|
||||
|
||||
@Query("""
|
||||
fun getForDate(profileId: Int, date: Date) = getRaw(SimpleSQLiteQuery("""
|
||||
$QUERY
|
||||
WHERE timetable.profileId = :profileId AND ((type != 3 AND date = :date) OR ((type = 3 OR type = 1) AND oldDate = :date))
|
||||
WHERE timetable.profileId = $profileId AND ((type != 3 AND date = "${date.stringY_m_d}") OR ((type = 3 OR type = 1) AND oldDate = "${date.stringY_m_d}"))
|
||||
ORDER BY id, type
|
||||
""")
|
||||
fun getForDate(profileId: Int, date: Date): LiveData<List<LessonFull>>
|
||||
"""))
|
||||
|
||||
@Query("""
|
||||
$QUERY
|
||||
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-2-22.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.dao
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.*
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.TimetableManual
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
@Dao
|
||||
interface TimetableManualDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun add(timetableManual: TimetableManual)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun addAll(timetableManualList: List<TimetableManual>)
|
||||
|
||||
@Query("SELECT * FROM timetableManual WHERE profileId = :profileId")
|
||||
fun getAll(profileId: Int): LiveData<List<TimetableManual>>
|
||||
|
||||
@Query("SELECT * FROM timetableManual WHERE profileId = :profileId AND date >= :dateFrom AND date <= :dateTo")
|
||||
fun getAllByDateRange(profileId: Int, dateFrom: Date, dateTo: Date): LiveData<List<TimetableManual>>
|
||||
|
||||
@Query("SELECT * FROM timetableManual WHERE profileId = :profileId AND (date IS NULL OR date = 0 OR date >= :dateFrom)")
|
||||
fun getAllToDisplay(profileId: Int, dateFrom: Date): LiveData<List<TimetableManual>>
|
||||
|
||||
@Delete
|
||||
fun delete(timetableManual: TimetableManual)
|
||||
|
||||
@Query("DELETE FROM timetableManual WHERE profileId = :profileId")
|
||||
fun clear(profileId: Int)
|
||||
}
|
@ -5,6 +5,7 @@ package pl.szczodrzynski.edziennik.data.db.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Ignore
|
||||
import androidx.room.Index
|
||||
|
||||
/*public Grade(int profileId, long id, String category, int color, String description, String name, float value, float weight, int semester, long teacherId, long subjectId) {
|
||||
@ -81,5 +82,11 @@ open class Grade(
|
||||
*/
|
||||
@ColumnInfo(name = "gradeIsImprovement")
|
||||
var isImprovement = false
|
||||
|
||||
@Ignore
|
||||
var showAsUnseen = false
|
||||
|
||||
val isImproved
|
||||
get() = parentId ?: -1L != -1L
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Ignore
|
||||
import androidx.room.Index
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
@ -66,6 +67,9 @@ open class Lesson(val profileId: Int, var id: Long) {
|
||||
|
||||
fun buildId(): Long = (displayDate?.combineWith(displayStartTime) ?: 0L) / 6L * 10L + (hashCode() and 0xFFFF)
|
||||
|
||||
@Ignore
|
||||
var showAsUnseen = false
|
||||
|
||||
override fun toString(): String {
|
||||
return "Lesson(profileId=$profileId, " +
|
||||
"id=$id, " +
|
||||
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-2-22.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
@Entity(tableName = "timetableManual",
|
||||
indices = [
|
||||
Index(value = ["profileId", "date"]),
|
||||
Index(value = ["profileId", "weekDay"])
|
||||
])
|
||||
class TimetableManual(
|
||||
val profileId: Int,
|
||||
var type: Int,
|
||||
var repeatBy: Int,
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Int = 0
|
||||
) {
|
||||
companion object {
|
||||
const val TYPE_NORMAL = 0
|
||||
const val TYPE_CANCELLED = 1
|
||||
const val TYPE_CHANGE = 2
|
||||
const val TYPE_SHIFTED_SOURCE = 3
|
||||
const val TYPE_SHIFTED_TARGET = 4
|
||||
const val TYPE_REMOVED = 5
|
||||
const val TYPE_CLASSROOM = 6
|
||||
const val REPEAT_WEEKLY = 0
|
||||
const val REPEAT_ONCE = 1
|
||||
const val REPEAT_BY_SUBJECT = 2
|
||||
}
|
||||
|
||||
// `date` for one time lesson
|
||||
@ColumnInfo(typeAffinity = ColumnInfo.INTEGER)
|
||||
var date: Date? = null
|
||||
// `weekDay` for repeating lesson (every week)
|
||||
var weekDay: Int? = null
|
||||
|
||||
var lessonNumber: Int? = null
|
||||
var startTime: Time? = null
|
||||
var endTime: Time? = null
|
||||
|
||||
var subjectId: Long? = null
|
||||
var teacherId: Long? = null
|
||||
var teamId: Long? = null
|
||||
var classroom: String? = null
|
||||
|
||||
fun verifyParams(): Boolean {
|
||||
return when (repeatBy) {
|
||||
REPEAT_WEEKLY -> date == null && weekDay != null
|
||||
REPEAT_ONCE -> date != null && weekDay == null
|
||||
REPEAT_BY_SUBJECT -> date == null && weekDay == null && subjectId != null
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.migration
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration79 : Migration(78, 79) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// manual timetable implementation
|
||||
database.execSQL("""CREATE TABLE timetableManual (
|
||||
profileId INTEGER NOT NULL,
|
||||
id INTEGER PRIMARY KEY NOT NULL,
|
||||
type INTEGER NOT NULL,
|
||||
repeatBy INTEGER NOT NULL DEFAULT 0,
|
||||
date INTEGER DEFAULT NULL,
|
||||
weekDay INTEGER DEFAULT NULL,
|
||||
lessonNumber INTEGER DEFAULT NULL,
|
||||
startTime TEXT DEFAULT NULL,
|
||||
endTime TEXT DEFAULT NULL,
|
||||
subjectId INTEGER DEFAULT NULL,
|
||||
teacherId INTEGER DEFAULT NULL,
|
||||
teamId INTEGER DEFAULT NULL,
|
||||
classroom TEXT DEFAULT NULL
|
||||
)""")
|
||||
database.execSQL("CREATE INDEX index_timetableManual_profileId_date ON timetableManual (profileId, date)")
|
||||
database.execSQL("CREATE INDEX index_timetableManual_profileId_weekDay ON timetableManual (profileId, weekDay)")
|
||||
}
|
||||
}
|
@ -90,7 +90,7 @@ class BellSyncDialog(
|
||||
}
|
||||
|
||||
launch {
|
||||
counterJob = startCoroutineTimer(repeatMillis = 1000) {
|
||||
counterJob = startCoroutineTimer(repeatMillis = 500) {
|
||||
val (bellDiff, multiplier) = actualBellDiff
|
||||
val bellDiffText = (if (multiplier == -1) '-' else '+') + bellDiff.stringHMS
|
||||
b.bellSyncHowto.text = app.getString(R.string.bell_sync_howto, bellTime.stringHM, bellDiffText)
|
||||
|
@ -12,24 +12,31 @@ import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AlertDialog.BUTTON_NEUTRAL
|
||||
import androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.Observer
|
||||
import com.google.android.material.datepicker.MaterialDatePicker
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialog
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
|
||||
import kotlinx.coroutines.*
|
||||
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.*
|
||||
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.full.EventFull
|
||||
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
|
||||
import pl.szczodrzynski.edziennik.databinding.DialogEventManualV2Binding
|
||||
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 pl.szczodrzynski.edziennik.utils.models.Week
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class EventManualDialog(
|
||||
@ -48,7 +55,7 @@ class EventManualDialog(
|
||||
private const val TAG = "EventManualDialog"
|
||||
}
|
||||
|
||||
private lateinit var job: Job
|
||||
private val job: Job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
@ -56,7 +63,6 @@ class EventManualDialog(
|
||||
private lateinit var b: DialogEventManualV2Binding
|
||||
private lateinit var dialog: AlertDialog
|
||||
|
||||
private lateinit var event: Event
|
||||
private var customColor: Int? = null
|
||||
private val editingShared = editingEvent?.sharedBy != null
|
||||
private val editingOwn = editingEvent?.sharedBy == "self"
|
||||
@ -67,11 +73,14 @@ class EventManualDialog(
|
||||
SzkolnyApi(app)
|
||||
}
|
||||
|
||||
private var enqueuedWeekDialog: AlertDialog? = null
|
||||
private var enqueuedWeekStart = Date.getToday()
|
||||
|
||||
init { run {
|
||||
if (activity.isFinishing)
|
||||
return@run
|
||||
job = Job()
|
||||
onShowListener?.invoke(TAG)
|
||||
EventBus.getDefault().register(this)
|
||||
b = DialogEventManualV2Binding.inflate(activity.layoutInflater)
|
||||
dialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.dialog_event_manual_title)
|
||||
@ -85,6 +94,7 @@ class EventManualDialog(
|
||||
}
|
||||
.setOnDismissListener {
|
||||
onDismissListener?.invoke(TAG)
|
||||
EventBus.getDefault().unregister(this@EventManualDialog)
|
||||
}
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
@ -104,12 +114,6 @@ class EventManualDialog(
|
||||
show()
|
||||
}
|
||||
|
||||
event = editingEvent?.clone() ?: Event().also { event ->
|
||||
event.profileId = profileId
|
||||
defaultType?.let {
|
||||
event.type = it
|
||||
}
|
||||
}
|
||||
b.shareSwitch.isChecked = editingShared
|
||||
b.shareSwitch.isEnabled = !editingShared || (editingShared && editingOwn)
|
||||
|
||||
@ -144,41 +148,138 @@ class EventManualDialog(
|
||||
else -> R.string.dialog_event_manual_share_first_notice
|
||||
}
|
||||
|
||||
b.shareDetails.setText(text, event.sharedByName ?: "")
|
||||
b.shareDetails.setText(text, editingEvent?.sharedByName ?: "")
|
||||
}
|
||||
|
||||
private fun syncTimetable(date: Date) {
|
||||
if (enqueuedWeekDialog != null) {
|
||||
return
|
||||
}
|
||||
if (app.profile.getStudentData("timetableNotPublic", false)) {
|
||||
return
|
||||
}
|
||||
val weekStart = date.weekStart
|
||||
enqueuedWeekDialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.please_wait)
|
||||
.setMessage(R.string.timetable_syncing_text)
|
||||
.setCancelable(false)
|
||||
.show()
|
||||
|
||||
enqueuedWeekStart = weekStart
|
||||
|
||||
EdziennikTask.syncProfile(
|
||||
profileId = App.profileId,
|
||||
viewIds = listOf(
|
||||
MainActivity.DRAWER_ITEM_TIMETABLE to 0
|
||||
),
|
||||
arguments = JsonObject(
|
||||
"weekStart" to weekStart.stringY_m_d
|
||||
)
|
||||
).enqueue(activity)
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onApiTaskFinishedEvent(event: ApiTaskFinishedEvent) {
|
||||
if (event.profileId == App.profileId) {
|
||||
enqueuedWeekDialog?.dismiss()
|
||||
enqueuedWeekDialog = null
|
||||
launch {
|
||||
b.timeDropdown.loadItems()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onApiTaskAllFinishedEvent(event: ApiTaskAllFinishedEvent) {
|
||||
enqueuedWeekDialog?.dismiss()
|
||||
enqueuedWeekDialog = null
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onApiTaskErrorEvent(event: ApiTaskErrorEvent) {
|
||||
dialog.dismiss()
|
||||
enqueuedWeekDialog?.dismiss()
|
||||
enqueuedWeekDialog = null
|
||||
}
|
||||
|
||||
private fun loadLists() { launch {
|
||||
with (b.dateDropdown) {
|
||||
db = app.db
|
||||
profileId = App.profileId
|
||||
showWeekDays = false
|
||||
showDays = true
|
||||
showOtherDate = true
|
||||
defaultLesson?.let {
|
||||
nextLessonSubjectId = it.displaySubjectId
|
||||
nextLessonSubjectName = it.displaySubjectName
|
||||
nextLessonTeamId = it.displayTeamId
|
||||
}
|
||||
loadItems()
|
||||
selectDefault(editingEvent?.eventDate)
|
||||
selectDefault(defaultLesson?.displayDate ?: defaultDate)
|
||||
onDateSelected = { date, lesson ->
|
||||
b.timeDropdown.deselect()
|
||||
b.timeDropdown.lessonsDate = date
|
||||
this@EventManualDialog.launch {
|
||||
if (!b.timeDropdown.loadItems())
|
||||
syncTimetable(date)
|
||||
lesson?.displayStartTime?.let { b.timeDropdown.selectTime(it) }
|
||||
lesson?.displaySubjectId?.let { b.subjectDropdown.selectSubject(it) } ?: b.subjectDropdown.deselect()
|
||||
lesson?.displayTeacherId?.let { b.teacherDropdown.selectTeacher(it) } ?: b.teacherDropdown.deselect()
|
||||
lesson?.displayTeamId?.let { b.teamDropdown.selectTeam(it) } ?: b.teamDropdown.selectTeamClass()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
with (b.timeDropdown) {
|
||||
db = app.db
|
||||
profileId = App.profileId
|
||||
showAllDay = true
|
||||
showCustomTime = true
|
||||
lessonsDate = b.dateDropdown.getSelected() as? Date ?: Date.getToday()
|
||||
displayMode = DISPLAY_LESSONS
|
||||
if (!loadItems())
|
||||
syncTimetable(lessonsDate ?: Date.getToday())
|
||||
selectDefault(editingEvent?.startTime)
|
||||
selectDefault(defaultLesson?.displayStartTime ?: defaultTime)
|
||||
onLessonSelected = { lesson ->
|
||||
lesson.displaySubjectId?.let { b.subjectDropdown.selectSubject(it) } ?: b.subjectDropdown.deselect()
|
||||
lesson.displayTeacherId?.let { b.teacherDropdown.selectTeacher(it) } ?: b.teacherDropdown.deselect()
|
||||
lesson.displayTeamId?.let { b.teamDropdown.selectTeam(it) } ?: b.teamDropdown.selectTeamClass()
|
||||
}
|
||||
}
|
||||
|
||||
with (b.teamDropdown) {
|
||||
db = app.db
|
||||
profileId = App.profileId
|
||||
showNoTeam = true
|
||||
loadItems()
|
||||
selectTeamClass()
|
||||
selectDefault(editingEvent?.teamId)
|
||||
selectDefault(defaultLesson?.displayTeamId)
|
||||
}
|
||||
|
||||
with (b.subjectDropdown) {
|
||||
db = app.db
|
||||
profileId = App.profileId
|
||||
showNoSubject = true
|
||||
showCustomSubject = false
|
||||
loadItems()
|
||||
selectDefault(editingEvent?.subjectId)
|
||||
selectDefault(defaultLesson?.displaySubjectId)
|
||||
}
|
||||
|
||||
with (b.teacherDropdown) {
|
||||
db = app.db
|
||||
profileId = App.profileId
|
||||
showNoTeacher = true
|
||||
loadItems()
|
||||
selectDefault(editingEvent?.teacherId)
|
||||
selectDefault(defaultLesson?.displayTeacherId)
|
||||
}
|
||||
|
||||
|
||||
val deferred = async(Dispatchers.Default) {
|
||||
// get the team list
|
||||
val teams = app.db.teamDao().getAllNow(profileId)
|
||||
b.teamDropdown.clear()
|
||||
b.teamDropdown += TextInputDropDown.Item(
|
||||
-1,
|
||||
activity.getString(R.string.dialog_event_manual_no_team),
|
||||
""
|
||||
)
|
||||
b.teamDropdown += teams.map { TextInputDropDown.Item(it.id, it.name, tag = it) }
|
||||
|
||||
// get the subject list
|
||||
val subjects = app.db.subjectDao().getAllNow(profileId)
|
||||
b.subjectDropdown.clear()
|
||||
b.subjectDropdown += TextInputDropDown.Item(
|
||||
-1,
|
||||
activity.getString(R.string.dialog_event_manual_no_subject),
|
||||
""
|
||||
)
|
||||
b.subjectDropdown += subjects.map { TextInputDropDown.Item(it.id, it.longName, tag = it) }
|
||||
|
||||
// get the teacher list
|
||||
val teachers = app.db.teacherDao().getAllNow(profileId)
|
||||
b.teacherDropdown.clear()
|
||||
b.teacherDropdown += TextInputDropDown.Item(
|
||||
-1,
|
||||
activity.getString(R.string.dialog_event_manual_no_teacher),
|
||||
""
|
||||
)
|
||||
b.teacherDropdown += teachers.map { TextInputDropDown.Item(it.id, it.fullName, tag = it) }
|
||||
|
||||
// get the event type list
|
||||
val eventTypes = app.db.eventTypeDao().getAllNow(profileId)
|
||||
b.typeDropdown.clear()
|
||||
@ -186,13 +287,10 @@ class EventManualDialog(
|
||||
}
|
||||
deferred.await()
|
||||
|
||||
b.teamDropdown.isEnabled = true
|
||||
b.subjectDropdown.isEnabled = true
|
||||
b.teacherDropdown.isEnabled = true
|
||||
b.typeDropdown.isEnabled = true
|
||||
|
||||
defaultType?.let {
|
||||
b.typeDropdown.select(it.toLong())
|
||||
b.typeDropdown.select(it)
|
||||
}
|
||||
|
||||
b.typeDropdown.selected?.let { item ->
|
||||
@ -201,9 +299,6 @@ class EventManualDialog(
|
||||
|
||||
// copy IDs from event being edited
|
||||
editingEvent?.let {
|
||||
b.teamDropdown.select(it.teamId)
|
||||
b.subjectDropdown.select(it.subjectId)
|
||||
b.teacherDropdown.select(it.teacherId)
|
||||
b.topic.setText(it.topic)
|
||||
b.typeDropdown.select(it.type.toLong())?.let { item ->
|
||||
customColor = (item.tag as EventType).color
|
||||
@ -215,8 +310,6 @@ class EventManualDialog(
|
||||
// copy IDs from the LessonFull
|
||||
defaultLesson?.let {
|
||||
b.teamDropdown.select(it.displayTeamId)
|
||||
b.subjectDropdown.select(it.displaySubjectId)
|
||||
b.teacherDropdown.select(it.displayTeacherId)
|
||||
}
|
||||
|
||||
b.typeDropdown.setOnChangeListener {
|
||||
@ -244,278 +337,8 @@ class EventManualDialog(
|
||||
})
|
||||
colorPickerDialog.show(activity.fragmentManager, "color-picker-dialog")
|
||||
}
|
||||
|
||||
loadDates()
|
||||
}}
|
||||
|
||||
private fun loadDates() { launch {
|
||||
val date = Date.getToday()
|
||||
val today = date.value
|
||||
var weekDay = date.weekDay
|
||||
|
||||
val deferred = async(Dispatchers.Default) {
|
||||
val dates = mutableListOf<TextInputDropDown.Item>()
|
||||
// item choosing the next lesson of specific subject
|
||||
b.subjectDropdown.selected?.let {
|
||||
if (it.tag is Subject) {
|
||||
dates += TextInputDropDown.Item(
|
||||
-it.id,
|
||||
activity.getString(R.string.dialog_event_manual_date_next_lesson, it.tag.longName)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TODAY
|
||||
dates += TextInputDropDown.Item(
|
||||
date.value.toLong(),
|
||||
activity.getString(R.string.dialog_event_manual_date_today, date.formattedString),
|
||||
tag = date.clone()
|
||||
)
|
||||
|
||||
// TOMORROW
|
||||
if (weekDay < 4) {
|
||||
date.stepForward(0, 0, 1)
|
||||
weekDay++
|
||||
dates += TextInputDropDown.Item(
|
||||
date.value.toLong(),
|
||||
activity.getString(R.string.dialog_event_manual_date_tomorrow, date.formattedString),
|
||||
tag = date.clone()
|
||||
)
|
||||
}
|
||||
// REMAINING SCHOOL DAYS OF THE CURRENT WEEK
|
||||
while (weekDay < 4) {
|
||||
date.stepForward(0, 0, 1) // step one day forward
|
||||
weekDay++
|
||||
dates += TextInputDropDown.Item(
|
||||
date.value.toLong(),
|
||||
activity.getString(R.string.dialog_event_manual_date_this_week, Week.getFullDayName(weekDay), date.formattedString),
|
||||
tag = date.clone()
|
||||
)
|
||||
}
|
||||
// go to next week Monday
|
||||
date.stepForward(0, 0, -weekDay + 7)
|
||||
weekDay = 0
|
||||
// ALL SCHOOL DAYS OF THE NEXT WEEK
|
||||
while (weekDay < 4) {
|
||||
dates += TextInputDropDown.Item(
|
||||
date.value.toLong(),
|
||||
activity.getString(R.string.dialog_event_manual_date_next_week, Week.getFullDayName(weekDay), date.formattedString),
|
||||
tag = date.clone()
|
||||
)
|
||||
date.stepForward(0, 0, 1) // step one day forward
|
||||
weekDay++
|
||||
}
|
||||
dates += TextInputDropDown.Item(
|
||||
-1L,
|
||||
activity.getString(R.string.dialog_event_manual_date_other)
|
||||
)
|
||||
dates
|
||||
}
|
||||
|
||||
val dates = deferred.await()
|
||||
b.dateDropdown.clear().append(dates)
|
||||
|
||||
defaultDate?.let {
|
||||
event.eventDate = it
|
||||
if (b.dateDropdown.select(it) == null)
|
||||
b.dateDropdown.select(TextInputDropDown.Item(
|
||||
it.value.toLong(),
|
||||
it.formattedString,
|
||||
tag = it
|
||||
))
|
||||
}
|
||||
|
||||
editingEvent?.eventDate?.let {
|
||||
b.dateDropdown.select(TextInputDropDown.Item(
|
||||
it.value.toLong(),
|
||||
it.formattedString,
|
||||
tag = it
|
||||
))
|
||||
}
|
||||
|
||||
defaultLesson?.displayDate?.let {
|
||||
b.dateDropdown.select(TextInputDropDown.Item(
|
||||
it.value.toLong(),
|
||||
it.formattedString,
|
||||
tag = it
|
||||
))
|
||||
}
|
||||
|
||||
if (b.dateDropdown.selected == null) {
|
||||
b.dateDropdown.select(today.toLong())
|
||||
}
|
||||
|
||||
b.dateDropdown.isEnabled = true
|
||||
|
||||
b.dateDropdown.setOnChangeListener { item ->
|
||||
when {
|
||||
// next lesson with specified subject
|
||||
item.id < -1 -> {
|
||||
val teamId = defaultLesson?.teamId ?: -1
|
||||
val selectedLessonDate = defaultLesson?.date ?: Date.getToday()
|
||||
|
||||
when (teamId) {
|
||||
-1L -> app.db.timetableDao().getNextWithSubject(profileId, selectedLessonDate, -item.id)
|
||||
else -> app.db.timetableDao().getNextWithSubjectAndTeam(profileId, selectedLessonDate, -item.id, teamId)
|
||||
}.observeOnce(activity, Observer {
|
||||
val lessonDate = it?.displayDate ?: return@Observer
|
||||
b.dateDropdown.select(TextInputDropDown.Item(
|
||||
lessonDate.value.toLong(),
|
||||
lessonDate.formattedString,
|
||||
tag = lessonDate
|
||||
))
|
||||
b.teamDropdown.select(it.displayTeamId)
|
||||
b.subjectDropdown.select(it.displaySubjectId)
|
||||
b.teacherDropdown.select(it.displayTeacherId)
|
||||
defaultLoaded = false
|
||||
loadHours(it.displayStartTime)
|
||||
})
|
||||
return@setOnChangeListener false
|
||||
}
|
||||
// custom date
|
||||
item.id == -1L -> {
|
||||
MaterialDatePicker.Builder
|
||||
.datePicker()
|
||||
.setSelection((b.dateDropdown.selectedId?.let { Date.fromValue(it.toInt()) }
|
||||
?: Date.getToday()).inMillis)
|
||||
.build()
|
||||
.apply {
|
||||
addOnPositiveButtonClickListener {
|
||||
val dateSelected = Date.fromMillis(it)
|
||||
b.dateDropdown.select(TextInputDropDown.Item(
|
||||
dateSelected.value.toLong(),
|
||||
dateSelected.formattedString,
|
||||
tag = dateSelected
|
||||
))
|
||||
loadHours()
|
||||
}
|
||||
show(this@EventManualDialog.activity.supportFragmentManager, "MaterialDatePicker")
|
||||
}
|
||||
|
||||
return@setOnChangeListener false
|
||||
}
|
||||
// a specific date
|
||||
else -> {
|
||||
b.dateDropdown.select(item)
|
||||
loadHours()
|
||||
}
|
||||
}
|
||||
return@setOnChangeListener true
|
||||
}
|
||||
|
||||
loadHours()
|
||||
}}
|
||||
|
||||
private fun loadHours(defaultHour: Time? = null) {
|
||||
b.timeDropdown.isEnabled = false
|
||||
// get the selected date
|
||||
val date = b.dateDropdown.selectedId?.let { Date.fromValue(it.toInt()) } ?: return
|
||||
// get all lessons for selected date
|
||||
app.db.timetableDao().getForDate(profileId, date).observeOnce(activity, Observer { lessons ->
|
||||
val hours = mutableListOf<TextInputDropDown.Item>()
|
||||
// add All day time choice
|
||||
hours += TextInputDropDown.Item(
|
||||
0L,
|
||||
activity.getString(R.string.dialog_event_manual_all_day)
|
||||
)
|
||||
lessons.forEach { lesson ->
|
||||
if (lesson.type == Lesson.TYPE_NO_LESSONS) {
|
||||
// indicate there are no lessons this day
|
||||
hours += TextInputDropDown.Item(
|
||||
-2L,
|
||||
activity.getString(R.string.dialog_event_manual_no_lessons)
|
||||
)
|
||||
return@forEach
|
||||
}
|
||||
// create the lesson caption
|
||||
val text = listOfNotEmpty(
|
||||
lesson.displayStartTime?.stringHM ?: "",
|
||||
lesson.displaySubjectName?.let {
|
||||
when {
|
||||
lesson.type == Lesson.TYPE_CANCELLED
|
||||
|| lesson.type == Lesson.TYPE_SHIFTED_SOURCE -> it.asStrikethroughSpannable()
|
||||
lesson.type != Lesson.TYPE_NORMAL -> it.asItalicSpannable()
|
||||
else -> it
|
||||
}
|
||||
} ?: ""
|
||||
)
|
||||
// add an item with LessonFull as the tag
|
||||
hours += TextInputDropDown.Item(
|
||||
lesson.displayStartTime?.value?.toLong() ?: -1,
|
||||
text.concat(" "),
|
||||
tag = lesson
|
||||
)
|
||||
}
|
||||
b.timeDropdown.clear().append(hours)
|
||||
|
||||
if (defaultLoaded) {
|
||||
b.timeDropdown.deselect()
|
||||
// select the TEAM_CLASS if possible
|
||||
b.teamDropdown.items.singleOrNull {
|
||||
it.tag is Team && it.tag.type == Team.TYPE_CLASS
|
||||
}?.let {
|
||||
b.teamDropdown.select(it)
|
||||
} ?: b.teamDropdown.deselect()
|
||||
|
||||
// clear subject, teacher selection
|
||||
b.subjectDropdown.deselect()
|
||||
b.teacherDropdown.deselect()
|
||||
}
|
||||
else {
|
||||
val setTime: (Time) -> Unit = {
|
||||
event.startTime = it
|
||||
if (b.timeDropdown.select(it) == null)
|
||||
b.timeDropdown.select(TextInputDropDown.Item(
|
||||
it.value.toLong(),
|
||||
it.stringHM,
|
||||
tag = it
|
||||
))
|
||||
}
|
||||
defaultTime?.let(setTime)
|
||||
editingEvent?.startTime?.let(setTime)
|
||||
defaultLesson?.displayStartTime?.let(setTime)
|
||||
defaultHour?.let(setTime)
|
||||
}
|
||||
defaultLoaded = true
|
||||
b.timeDropdown.isEnabled = true
|
||||
|
||||
// attach a listener to time dropdown
|
||||
b.timeDropdown.setOnChangeListener { item ->
|
||||
when (item.id) {
|
||||
// no lessons this day
|
||||
-2L -> {
|
||||
b.timeDropdown.deselect()
|
||||
return@setOnChangeListener false
|
||||
}
|
||||
|
||||
// custom start hour
|
||||
-1L -> return@setOnChangeListener false
|
||||
|
||||
// selected a specific lesson
|
||||
else -> {
|
||||
if (item.tag is LessonFull) {
|
||||
// update team, subject, teacher dropdowns,
|
||||
// using the LessonFull from item tag
|
||||
b.teamDropdown.deselect()
|
||||
b.subjectDropdown.deselect()
|
||||
b.teacherDropdown.deselect()
|
||||
item.tag.displayTeamId?.let {
|
||||
b.teamDropdown.select(it)
|
||||
}
|
||||
item.tag.displaySubjectId?.let {
|
||||
b.subjectDropdown.select(it)
|
||||
}
|
||||
item.tag.displayTeacherId?.let {
|
||||
b.teacherDropdown.select(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return@setOnChangeListener true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun showRemoveEventDialog() {
|
||||
val shareNotice = when {
|
||||
editingShared && editingOwn -> "\n\n"+activity.getString(R.string.dialog_event_manual_remove_shared_self)
|
||||
@ -541,22 +364,29 @@ class EventManualDialog(
|
||||
}
|
||||
|
||||
private fun saveEvent() {
|
||||
val date = b.dateDropdown.selected?.tag.instanceOfOrNull<Date>()
|
||||
val startTime = b.timeDropdown.selected?.tag.instanceOfOrNull<Time>()
|
||||
val teamId = b.teamDropdown.selected?.id
|
||||
val date = b.dateDropdown.getSelected() as? Date
|
||||
val startTimePair = b.timeDropdown.getSelected() as? Pair<*, *>
|
||||
val startTime = startTimePair?.first as? Time
|
||||
val teamId = b.teamDropdown.getSelected() as? Long
|
||||
val type = b.typeDropdown.selected?.id
|
||||
val topic = b.topic.text?.toString()
|
||||
val subjectId = b.subjectDropdown.selected?.id
|
||||
val teacherId = b.teacherDropdown.selected?.id
|
||||
val subjectId = b.subjectDropdown.getSelected() as? Long
|
||||
val teacherId = b.teacherDropdown.getSelected() as? Long
|
||||
|
||||
val share = b.shareSwitch.isChecked
|
||||
|
||||
b.dateDropdown.error = null
|
||||
b.teamDropdown.error = null
|
||||
b.typeDropdown.error = null
|
||||
b.topic.error = null
|
||||
|
||||
var isError = false
|
||||
|
||||
if (date == null) {
|
||||
b.dateDropdown.error = app.getString(R.string.dialog_event_manual_date_choose)
|
||||
isError = true
|
||||
}
|
||||
|
||||
if (share && teamId == null) {
|
||||
b.teamDropdown.error = app.getString(R.string.dialog_event_manual_team_choose)
|
||||
isError = true
|
||||
|
@ -3,6 +3,7 @@ package pl.szczodrzynski.edziennik.ui.dialogs.grade
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.*
|
||||
@ -10,7 +11,9 @@ import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
||||
import pl.szczodrzynski.edziennik.databinding.DialogGradeDetailsBinding
|
||||
import pl.szczodrzynski.edziennik.onClick
|
||||
import pl.szczodrzynski.edziennik.setTintColor
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter
|
||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
@ -50,16 +53,22 @@ class GradeDetailsDialog(
|
||||
.show()
|
||||
val manager = app.gradesManager
|
||||
|
||||
val gradeColor = manager.getColor(grade)
|
||||
val gradeColor = manager.getGradeColor(grade)
|
||||
b.grade = grade
|
||||
b.weightText = manager.getWeightString(app, grade)
|
||||
b.commentVisible = false
|
||||
b.devMode = App.debugMode
|
||||
b.gradeName.setTextColor(if (ColorUtils.calculateLuminance(gradeColor) > 0.3) -0x1000000 else -0x1)
|
||||
b.gradeName.setTextColor(if (ColorUtils.calculateLuminance(gradeColor) > 0.3) 0x99000000.toInt() else 0x99ffffff.toInt())
|
||||
b.gradeName.background.setTintColor(gradeColor)
|
||||
|
||||
b.gradeValue = if (grade.weight == 0f || grade.value < 0f) -1f else manager.getGradeValue(grade)
|
||||
|
||||
b.customValueDivider.isVisible = manager.plusValue != null || manager.minusValue != null
|
||||
b.customValueLayout.isVisible = b.customValueDivider.isVisible
|
||||
b.customValueButton.onClick {
|
||||
GradesConfigDialog(activity, reloadOnDismiss = true)
|
||||
}
|
||||
|
||||
launch {
|
||||
val historyList = withContext(Dispatchers.Default) {
|
||||
app.db.gradeDao().getAllWithParentIdNow(App.profileId, grade.id)
|
||||
|
@ -89,6 +89,8 @@ class GradesConfigDialog(
|
||||
}?.isChecked = true
|
||||
|
||||
b.dontCountZeroToAverage.isChecked = !profileConfig.countZeroToAvg
|
||||
b.hideImproved.isChecked = profileConfig.hideImproved
|
||||
b.averageWithoutWeight.isChecked = profileConfig.averageWithoutWeight
|
||||
}
|
||||
|
||||
private fun saveConfig() {
|
||||
@ -125,6 +127,16 @@ class GradesConfigDialog(
|
||||
b.gradeAverageMode2.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_1_AVG_2_SEM }
|
||||
b.gradeAverageMode3.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_1_SEM_2_SEM }
|
||||
|
||||
b.dontCountZeroToAverage.setOnCheckedChangeListener { _, isChecked -> profileConfig.countZeroToAvg = !isChecked }
|
||||
b.dontCountZeroToAverage.onChange { _, isChecked -> profileConfig.countZeroToAvg = !isChecked }
|
||||
b.hideImproved.onChange { _, isChecked -> profileConfig.hideImproved = isChecked }
|
||||
b.averageWithoutWeight.onChange { _, isChecked -> profileConfig.averageWithoutWeight = isChecked }
|
||||
|
||||
b.averageWithoutWeightHelp.onClick {
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.grades_config_average_without_weight)
|
||||
.setMessage(R.string.grades_config_average_without_weight_message)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,14 @@ import androidx.core.content.FileProvider
|
||||
import com.google.android.material.datepicker.MaterialDatePicker
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
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.db.entity.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
|
||||
import pl.szczodrzynski.edziennik.databinding.DialogGenerateBlockTimetableBinding
|
||||
@ -53,7 +60,7 @@ class GenerateBlockTimetableDialog(
|
||||
|
||||
private val app by lazy { activity.application as App }
|
||||
|
||||
private lateinit var job: Job
|
||||
private val job: Job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
@ -64,11 +71,15 @@ class GenerateBlockTimetableDialog(
|
||||
private var showTeachersNames: Boolean = true
|
||||
private var noColors: Boolean = false
|
||||
|
||||
private var enqueuedWeekDialog: AlertDialog? = null
|
||||
private var enqueuedWeekStart = Date.getToday()
|
||||
private var enqueuedWeekEnd = Date.getToday()
|
||||
|
||||
init { run {
|
||||
if (activity.isFinishing)
|
||||
return@run
|
||||
job = Job()
|
||||
onShowListener?.invoke(TAG)
|
||||
EventBus.getDefault().register(this)
|
||||
|
||||
val weekCurrentStart = Week.getWeekStart()
|
||||
val weekCurrentEnd = Week.getWeekEnd()
|
||||
@ -88,16 +99,20 @@ class GenerateBlockTimetableDialog(
|
||||
.setTitle(R.string.timetable_generate_range)
|
||||
.setView(b.root)
|
||||
.setNeutralButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
|
||||
.setPositiveButton(R.string.save) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
.setPositiveButton(R.string.save, null)
|
||||
.setOnDismissListener {
|
||||
onDismissListener?.invoke(TAG)
|
||||
EventBus.getDefault().unregister(this@GenerateBlockTimetableDialog)
|
||||
}
|
||||
.show()
|
||||
|
||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE)?.onClick {
|
||||
when (b.weekSelectionRadioGroup.checkedRadioButtonId) {
|
||||
R.id.withChangesCurrentWeekRadio -> generateBlockTimetable(weekCurrentStart, weekCurrentEnd)
|
||||
R.id.withChangesNextWeekRadio -> generateBlockTimetable(weekNextStart, weekNextEnd)
|
||||
R.id.forSelectedWeekRadio -> selectDate()
|
||||
}
|
||||
}
|
||||
.setOnDismissListener { onDismissListener?.invoke(TAG) }
|
||||
.show()
|
||||
}}
|
||||
|
||||
private fun selectDate() {
|
||||
@ -115,12 +130,26 @@ class GenerateBlockTimetableDialog(
|
||||
.show(activity.supportFragmentManager, "MaterialDatePicker")
|
||||
}
|
||||
|
||||
private fun generateBlockTimetable(weekStart: Date, weekEnd: Date) { launch {
|
||||
val progressDialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.timetable_generate_progress_title)
|
||||
.setMessage(R.string.timetable_generate_progress_text)
|
||||
.show()
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onApiTaskFinishedEvent(event: ApiTaskFinishedEvent) {
|
||||
if (event.profileId == App.profileId) {
|
||||
enqueuedWeekDialog?.dismiss()
|
||||
generateBlockTimetable(enqueuedWeekStart, enqueuedWeekEnd)
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onApiTaskAllFinishedEvent(event: ApiTaskAllFinishedEvent) {
|
||||
enqueuedWeekDialog?.dismiss()
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onApiTaskErrorEvent(event: ApiTaskErrorEvent) {
|
||||
dialog.dismiss()
|
||||
enqueuedWeekDialog?.dismiss()
|
||||
}
|
||||
|
||||
private fun generateBlockTimetable(weekStart: Date, weekEnd: Date) { launch {
|
||||
val weekDays = mutableListOf<MutableList<Lesson>>()
|
||||
for (i in weekStart.weekDay..weekEnd.weekDay) {
|
||||
weekDays.add(mutableListOf())
|
||||
@ -157,12 +186,46 @@ class GenerateBlockTimetableDialog(
|
||||
return@mapNotNull lesson
|
||||
}
|
||||
|
||||
if (lessons.isEmpty()) {
|
||||
if (enqueuedWeekDialog != null) {
|
||||
return@launch
|
||||
}
|
||||
enqueuedWeekDialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.please_wait)
|
||||
.setMessage(R.string.timetable_syncing_text)
|
||||
.setCancelable(false)
|
||||
.show()
|
||||
|
||||
enqueuedWeekStart = weekStart
|
||||
enqueuedWeekEnd = weekEnd
|
||||
|
||||
EdziennikTask.syncProfile(
|
||||
profileId = App.profileId,
|
||||
viewIds = listOf(
|
||||
MainActivity.DRAWER_ITEM_TIMETABLE to 0
|
||||
),
|
||||
arguments = JsonObject(
|
||||
"weekStart" to weekStart.stringY_m_d
|
||||
)
|
||||
).enqueue(activity)
|
||||
return@launch
|
||||
}
|
||||
|
||||
val progressDialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.timetable_generate_progress_title)
|
||||
.setMessage(R.string.timetable_generate_progress_text)
|
||||
.show()
|
||||
|
||||
if (minTime == null) {
|
||||
progressDialog.dismiss()
|
||||
// TODO: Toast
|
||||
return@launch
|
||||
}
|
||||
|
||||
dialog.dismiss()
|
||||
|
||||
val uri = withContext(Dispatchers.Default) {
|
||||
|
||||
val diff = Time.diff(maxTime, minTime)
|
||||
|
||||
val imageWidth = WIDTH_CONSTANT + maxWeekDay * (WIDTH_WEEKDAY + WIDTH_SPACING) - WIDTH_SPACING
|
||||
@ -220,11 +283,13 @@ class GenerateBlockTimetableDialog(
|
||||
if (!showTeachersNames) teacherName.visibility = View.GONE
|
||||
|
||||
when (lesson.type) {
|
||||
Lesson.TYPE_NORMAL -> {}
|
||||
Lesson.TYPE_NORMAL -> {
|
||||
}
|
||||
Lesson.TYPE_CANCELLED, Lesson.TYPE_SHIFTED_SOURCE -> {
|
||||
card.setCardBackgroundColor(Color.BLACK)
|
||||
subjectName.setTextColor(Color.WHITE)
|
||||
subjectName.text = lesson.displaySubjectName?.asStrikethroughSpannable() ?: ""
|
||||
subjectName.text = lesson.displaySubjectName?.asStrikethroughSpannable()
|
||||
?: ""
|
||||
}
|
||||
else -> {
|
||||
card.setCardBackgroundColor(0xff234158.toInt())
|
||||
@ -318,7 +383,7 @@ class GenerateBlockTimetableDialog(
|
||||
fos.close()
|
||||
} catch (e: Exception) {
|
||||
Log.e("SAVE_IMAGE", e.message, e)
|
||||
return@launch
|
||||
return@withContext null
|
||||
}
|
||||
|
||||
val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
@ -326,6 +391,8 @@ class GenerateBlockTimetableDialog(
|
||||
} else {
|
||||
Uri.parse("file://" + outputFile.absolutePath)
|
||||
}
|
||||
uri
|
||||
}
|
||||
|
||||
progressDialog.dismiss()
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
|
@ -25,7 +25,6 @@ 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.ui.modules.timetable.TimetableUtils
|
||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||
@ -50,7 +49,7 @@ class LessonDetailsDialog(
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
private lateinit var adapter: EventListAdapter
|
||||
private val utils by lazy { TimetableUtils() }
|
||||
private val manager by lazy { app.timetableManager }
|
||||
|
||||
init { run {
|
||||
if (activity.isFinishing)
|
||||
@ -91,7 +90,7 @@ class LessonDetailsDialog(
|
||||
val lessonTime = lesson.displayStartTime ?: return
|
||||
b.lessonDate.text = Week.getFullDayName(lessonDate.weekDay) + ", " + lessonDate.formattedString
|
||||
|
||||
b.annotationVisible = utils.getAnnotation(activity, lesson, b.annotation)
|
||||
b.annotationVisible = manager.getAnnotation(activity, lesson, b.annotation)
|
||||
|
||||
if (lesson.type >= Lesson.TYPE_SHIFTED_SOURCE) {
|
||||
b.shiftedLayout.visibility = View.VISIBLE
|
||||
|
@ -126,6 +126,8 @@ class AgendaFragment : Fragment(), CoroutineScope {
|
||||
}
|
||||
|
||||
private fun createDefaultAgendaView() { (b as? FragmentAgendaDefaultBinding)?.let { b -> launch {
|
||||
if (!isAdded)
|
||||
return@launch
|
||||
delay(500)
|
||||
|
||||
val eventList = mutableListOf<CalendarEvent>()
|
||||
@ -139,6 +141,8 @@ class AgendaFragment : Fragment(), CoroutineScope {
|
||||
/**
|
||||
* LESSON CHANGES
|
||||
*/
|
||||
if (!isAdded)
|
||||
return@launch
|
||||
|
||||
val lessons = withContext(Dispatchers.Default) { app.db.timetableDao().getAllChangesNow(app.profileId) }
|
||||
val lessonChangeCounters = mutableListOf<LessonChangeCounter>()
|
||||
@ -170,6 +174,8 @@ class AgendaFragment : Fragment(), CoroutineScope {
|
||||
/**
|
||||
* TEACHER ABSENCES
|
||||
*/
|
||||
if (!isAdded)
|
||||
return@launch
|
||||
|
||||
val showTeacherAbsences = app.profile.getStudentData("showTeacherAbsences", true)
|
||||
|
||||
@ -208,6 +214,8 @@ class AgendaFragment : Fragment(), CoroutineScope {
|
||||
/**
|
||||
* EVENTS
|
||||
*/
|
||||
if (!isAdded)
|
||||
return@launch
|
||||
|
||||
val events = withContext(Dispatchers.Default) { app.db.eventDao().getAllNow(app.profileId) }
|
||||
val unreadEventDates = mutableSetOf<Int>()
|
||||
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-2-22.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.base
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
|
||||
abstract class PagerFragment : Fragment() {
|
||||
private var isPageCreated = false
|
||||
|
||||
/**
|
||||
* Called when the page is first shown, or if previous
|
||||
* [onPageCreated] returned false
|
||||
*
|
||||
* @return true if the view is set up
|
||||
* @return false if the setup failed. The method may be then called
|
||||
* again, when page becomes visible.
|
||||
*/
|
||||
abstract fun onPageCreated(): Boolean
|
||||
|
||||
override fun onResume() {
|
||||
if (!isPageCreated) {
|
||||
isPageCreated = onPageCreated()
|
||||
}
|
||||
super.onResume()
|
||||
}
|
||||
}
|
@ -48,6 +48,7 @@ class ErrorSnackbar(val activity: AppCompatActivity) : CoroutineScope {
|
||||
fun addError(apiError: ApiError): ErrorSnackbar {
|
||||
errors.add(apiError)
|
||||
snackbar?.setText(apiError.getStringReason(activity))
|
||||
snackbar?.duration = 15000
|
||||
return this
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,6 @@ package pl.szczodrzynski.edziennik.ui.modules.grades
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.text.TextUtils
|
||||
import android.util.AttributeSet
|
||||
import android.util.TypedValue.COMPLEX_UNIT_SP
|
||||
@ -48,7 +47,7 @@ class GradeView : AppCompatTextView {
|
||||
|
||||
val gradeName = grade.name
|
||||
|
||||
val gradeColor = manager.getColor(grade)
|
||||
val gradeColor = manager.getGradeColor(grade)
|
||||
|
||||
text = if (periodGradesTextual)
|
||||
when (grade.type) {
|
||||
@ -83,7 +82,7 @@ class GradeView : AppCompatTextView {
|
||||
0x99ffffff.toInt()
|
||||
})
|
||||
|
||||
typeface = Typeface.create("serif-monospace", Typeface.BOLD)
|
||||
//typeface = Typeface.create("sans-serif-light", Typeface.NORMAL)
|
||||
setBackgroundResource(when (grade.type) {
|
||||
TYPE_SEMESTER1_PROPOSED,
|
||||
TYPE_SEMESTER2_PROPOSED,
|
||||
@ -104,7 +103,7 @@ class GradeView : AppCompatTextView {
|
||||
setPadding(2.dp, 2.dp, 2.dp, 2.dp)
|
||||
}
|
||||
else {
|
||||
setTextSize(COMPLEX_UNIT_SP, 16f)
|
||||
setTextSize(COMPLEX_UNIT_SP, 14f)
|
||||
setPadding(5.dp, 0, 5.dp, 0)
|
||||
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
|
||||
setMargins(0, 0, 5.dp, 0)
|
||||
|
@ -10,19 +10,24 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
||||
import pl.szczodrzynski.edziennik.onClick
|
||||
import pl.szczodrzynski.edziennik.startCoroutineTimer
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.models.*
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.*
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class GradesAdapter(
|
||||
val activity: AppCompatActivity,
|
||||
var onGradeClick: ((item: GradeFull) -> Unit)? = null,
|
||||
var onGradesEditorClick: ((subject: GradesSubject, semester: GradesSemester) -> Unit)? = null
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), CoroutineScope {
|
||||
companion object {
|
||||
private const val TAG = "GradesAdapter"
|
||||
private const val ITEM_TYPE_SUBJECT = 0
|
||||
@ -34,6 +39,13 @@ class GradesAdapter(
|
||||
const val STATE_OPENED = 1
|
||||
}
|
||||
|
||||
private val app = activity.applicationContext as App
|
||||
private val manager = app.gradesManager
|
||||
|
||||
private val job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
var items = mutableListOf<Any>()
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
@ -67,13 +79,17 @@ class GradesAdapter(
|
||||
}
|
||||
if (model !is ExpandableItemModel<*>)
|
||||
return@OnClickListener
|
||||
expandModel(model, view)
|
||||
}
|
||||
|
||||
fun expandModel(model: ExpandableItemModel<*>?, view: View?, notifyAdapter: Boolean = true) {
|
||||
model ?: return
|
||||
val position = items.indexOf(model)
|
||||
if (position == -1)
|
||||
return@OnClickListener
|
||||
//val position = it.getTag(R.string.tag_key_position) as? Int ?: return@OnClickListener
|
||||
return
|
||||
|
||||
if (model is GradesSubject || model is GradesSemester) {
|
||||
view.findViewById<View>(R.id.dropdownIcon)?.let { dropdownIcon ->
|
||||
view?.findViewById<View>(R.id.dropdownIcon)?.let { dropdownIcon ->
|
||||
ObjectAnimator.ofFloat(
|
||||
dropdownIcon,
|
||||
View.ROTATION,
|
||||
@ -83,40 +99,43 @@ class GradesAdapter(
|
||||
}
|
||||
}
|
||||
if (model is GradesSubject) {
|
||||
val preview = view.findViewById<View>(R.id.previewContainer)
|
||||
val summary = view.findViewById<View>(R.id.yearSummary)
|
||||
val preview = view?.findViewById<View>(R.id.previewContainer)
|
||||
val summary = view?.findViewById<View>(R.id.yearSummary)
|
||||
preview?.visibility = if (model.state == STATE_CLOSED) View.INVISIBLE else View.VISIBLE
|
||||
summary?.visibility = if (model.state == STATE_CLOSED) View.VISIBLE else View.INVISIBLE
|
||||
}
|
||||
|
||||
if (model.state == STATE_CLOSED) {
|
||||
|
||||
val subItems = if (model is GradesSemester && model.grades.isEmpty())
|
||||
val subItems = when {
|
||||
model is GradesSemester && model.grades.isEmpty() ->
|
||||
listOf(GradesEmpty())
|
||||
else
|
||||
model.items
|
||||
model is GradesSemester && manager.hideImproved ->
|
||||
model.items.filter { !it.seen || !it.isImproved }
|
||||
else -> model.items
|
||||
}
|
||||
|
||||
model.state = STATE_OPENED
|
||||
items.addAll(position + 1, subItems.filterNotNull())
|
||||
notifyItemRangeInserted(position + 1, subItems.size)
|
||||
/*notifyItemRangeChanged(
|
||||
position + subItems.size,
|
||||
items.size - (position + subItems.size)
|
||||
)*/
|
||||
//notifyItemRangeChanged(position, items.size - position)
|
||||
if (notifyAdapter) notifyItemRangeInserted(position + 1, subItems.size)
|
||||
|
||||
if (model is GradesSubject) {
|
||||
// auto expand first semester
|
||||
if (model.semesters.isNotEmpty()) {
|
||||
val semester = model.semesters.firstOrNull { it.grades.isNotEmpty() } ?: model.semesters.first()
|
||||
val semesterIndex = model.semesters.indexOf(semester)
|
||||
val grades = if (semester.grades.isEmpty())
|
||||
|
||||
val grades = when {
|
||||
semester.grades.isEmpty() ->
|
||||
listOf(GradesEmpty())
|
||||
else
|
||||
semester.grades
|
||||
manager.hideImproved ->
|
||||
semester.grades.filter { !it.seen || !it.isImproved }
|
||||
else -> semester.grades
|
||||
}
|
||||
|
||||
semester.state = STATE_OPENED
|
||||
items.addAll(position + 2 + semesterIndex, grades)
|
||||
notifyItemRangeInserted(position + 2 + semesterIndex, grades.size)
|
||||
if (notifyAdapter) notifyItemRangeInserted(position + 2 + semesterIndex, grades.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -138,9 +157,7 @@ class GradesAdapter(
|
||||
|
||||
if (end != -1) {
|
||||
items.subList(start, end).clear()
|
||||
notifyItemRangeRemoved(start, end - start)
|
||||
//notifyItemRangeChanged(start, end - start)
|
||||
//notifyItemRangeChanged(position, items.size - position)
|
||||
if (notifyAdapter) notifyItemRangeRemoved(start, end - start)
|
||||
}
|
||||
|
||||
model.state = STATE_CLOSED
|
||||
@ -152,8 +169,6 @@ class GradesAdapter(
|
||||
if (holder !is BindableViewHolder<*>)
|
||||
return
|
||||
|
||||
val app = activity.applicationContext as App
|
||||
|
||||
val viewType = when (holder) {
|
||||
is SubjectViewHolder -> ITEM_TYPE_SUBJECT
|
||||
is SemesterViewHolder -> ITEM_TYPE_SEMESTER
|
||||
@ -167,11 +182,11 @@ class GradesAdapter(
|
||||
holder.itemView.setTag(R.string.tag_key_model, item)
|
||||
|
||||
when {
|
||||
holder is SubjectViewHolder && item is GradesSubject -> holder.onBind(activity, app, item, position)
|
||||
holder is SemesterViewHolder && item is GradesSemester -> holder.onBind(activity, app, item, position)
|
||||
holder is EmptyViewHolder && item is GradesEmpty -> holder.onBind(activity, app, item, position)
|
||||
holder is GradeViewHolder && item is GradeFull -> holder.onBind(activity, app, item, position)
|
||||
holder is StatsViewHolder && item is GradesStats -> holder.onBind(activity, app, item, position)
|
||||
holder is SubjectViewHolder && item is GradesSubject -> holder.onBind(activity, app, item, position, this)
|
||||
holder is SemesterViewHolder && item is GradesSemester -> holder.onBind(activity, app, item, position, this)
|
||||
holder is EmptyViewHolder && item is GradesEmpty -> holder.onBind(activity, app, item, position, this)
|
||||
holder is GradeViewHolder && item is GradeFull -> holder.onBind(activity, app, item, position, this)
|
||||
holder is StatsViewHolder && item is GradesStats -> holder.onBind(activity, app, item, position, this)
|
||||
}
|
||||
|
||||
if (holder is SemesterViewHolder && item is GradesSemester) {
|
||||
@ -184,5 +199,23 @@ class GradesAdapter(
|
||||
holder.itemView.setOnClickListener(onClickListener)
|
||||
}
|
||||
|
||||
fun notifyItemChanged(model: Any) {
|
||||
startCoroutineTimer(1000L, 0L) {
|
||||
val index = items.indexOf(model)
|
||||
if (index != -1)
|
||||
notifyItemChanged(index)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeItem(model: Any) {
|
||||
startCoroutineTimer(2000L, 0L) {
|
||||
val index = items.indexOf(model)
|
||||
if (index != -1) {
|
||||
items.removeAt(index)
|
||||
notifyItemRemoved(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
}
|
||||
|
@ -4,31 +4,37 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.grades
|
||||
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial.Icon2
|
||||
import kotlinx.coroutines.*
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.Bundle
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.TARGET_GRADES_EDITOR
|
||||
import pl.szczodrzynski.edziennik.averageOrNull
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_GRADE
|
||||
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
||||
import pl.szczodrzynski.edziennik.databinding.GradesFragmentBinding
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradeDetailsDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesAverages
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSemester
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesStats
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSubject
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.math.max
|
||||
|
||||
|
||||
class GradesFragment : Fragment(), CoroutineScope {
|
||||
companion object {
|
||||
private const val TAG = "GradesFragment"
|
||||
@ -48,6 +54,7 @@ class GradesFragment : Fragment(), CoroutineScope {
|
||||
}
|
||||
private val manager by lazy { app.gradesManager }
|
||||
private val dontCountGrades by lazy { manager.dontCountGrades }
|
||||
private var expandSubjectId = 0L
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
activity = (getActivity() as MainActivity?) ?: return null
|
||||
@ -62,6 +69,8 @@ class GradesFragment : Fragment(), CoroutineScope {
|
||||
if (!isAdded)
|
||||
return
|
||||
|
||||
expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L
|
||||
|
||||
app.db.gradeDao()
|
||||
.getAllOrderBy(App.profileId, app.gradesManager.getOrderByString())
|
||||
.observe(this, Observer { grades ->
|
||||
@ -111,8 +120,29 @@ class GradesFragment : Fragment(), CoroutineScope {
|
||||
"finalOtherSemester" to otherSemester?.finalGrade?.value
|
||||
))
|
||||
}
|
||||
|
||||
activity.bottomSheet.prependItems(
|
||||
BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_grades_config)
|
||||
.withIcon(Icon2.cmd_settings_outline)
|
||||
.withOnClickListener(View.OnClickListener {
|
||||
activity.bottomSheet.close()
|
||||
GradesConfigDialog(activity, true, null, null)
|
||||
}),
|
||||
BottomSheetSeparatorItem(true),
|
||||
BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_mark_as_read)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
|
||||
.withOnClickListener(View.OnClickListener {
|
||||
activity.bottomSheet.close()
|
||||
AsyncTask.execute { App.db.metadataDao().setAllSeen(App.profileId, TYPE_GRADE, true) }
|
||||
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show()
|
||||
})
|
||||
)
|
||||
activity.gainAttention()
|
||||
}
|
||||
|
||||
@Suppress("SuspendFunctionOnCoroutineScope")
|
||||
private suspend fun processGrades(grades: List<GradeFull>) {
|
||||
val items = mutableListOf<GradesSubject>()
|
||||
|
||||
@ -121,6 +151,8 @@ class GradesFragment : Fragment(), CoroutineScope {
|
||||
var subject = GradesSubject(subjectId, "")
|
||||
var semester = GradesSemester(0, 1)
|
||||
|
||||
val hideImproved = manager.hideImproved
|
||||
|
||||
// grades returned by the query are ordered
|
||||
// by the subject ID, so it's easier and probably
|
||||
// a bit faster to build all the models
|
||||
@ -144,6 +176,11 @@ class GradesFragment : Fragment(), CoroutineScope {
|
||||
?: GradesSemester(subject.subjectId, grade.semester).also { subject.semesters += it }
|
||||
}
|
||||
|
||||
grade.showAsUnseen = !grade.seen
|
||||
if (!grade.seen) {
|
||||
semester.hasUnseen = true
|
||||
}
|
||||
|
||||
when (grade.type) {
|
||||
Grade.TYPE_SEMESTER1_PROPOSED,
|
||||
Grade.TYPE_SEMESTER2_PROPOSED -> semester.proposedGrade = grade
|
||||
@ -237,9 +274,27 @@ class GradesFragment : Fragment(), CoroutineScope {
|
||||
adapter.items = items.toMutableList()
|
||||
adapter.items.add(stats)
|
||||
|
||||
var expandSubjectModel: GradesSubject? = null
|
||||
if (expandSubjectId != 0L) {
|
||||
expandSubjectModel = items.firstOrNull { it.subjectId == expandSubjectId }
|
||||
adapter.expandModel(
|
||||
model = expandSubjectModel,
|
||||
view = null,
|
||||
notifyAdapter = false
|
||||
)
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
startCoroutineTimer(500L, 0L) {
|
||||
if (expandSubjectModel != null) {
|
||||
b.gradesRecyclerView.smoothScrollToPosition(
|
||||
items.indexOf(expandSubjectModel) + expandSubjectModel.semesters.size + (expandSubjectModel.semesters.firstOrNull()?.grades?.size ?: 0)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun countGrade(grade: Grade, averages: GradesAverages) {
|
||||
@ -247,8 +302,13 @@ class GradesFragment : Fragment(), CoroutineScope {
|
||||
val weight = manager.getGradeWeight(dontCountGrades, grade)
|
||||
when (grade.type) {
|
||||
Grade.TYPE_NORMAL -> {
|
||||
if (grade.value > 0f) {
|
||||
// count to the arithmetic average
|
||||
// only if value more than 0
|
||||
// to exclude "+", "-", "np" etc.
|
||||
averages.normalSum += value
|
||||
averages.normalCount++
|
||||
}
|
||||
averages.normalWeightedSum += value * weight
|
||||
averages.normalWeightedCount += weight
|
||||
}
|
||||
|
@ -4,16 +4,17 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.grades.models
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
||||
|
||||
data class GradesSemester(
|
||||
val subjectId: Long,
|
||||
val number: Int,
|
||||
val grades: MutableList<Grade> = mutableListOf()
|
||||
) : ExpandableItemModel<Grade>(grades) {
|
||||
val grades: MutableList<GradeFull> = mutableListOf()
|
||||
) : ExpandableItemModel<GradeFull>(grades) {
|
||||
override var level = 2
|
||||
|
||||
var hasUnseen = false
|
||||
|
||||
val averages = GradesAverages()
|
||||
var proposedGrade: GradeFull? = null
|
||||
var finalGrade: GradeFull? = null
|
||||
|
@ -16,6 +16,9 @@ data class GradesSubject(
|
||||
var lastAddedDate = 0L
|
||||
var semester: Int = 1
|
||||
|
||||
val hasUnseen
|
||||
get() = semesters.any { it.hasUnseen }
|
||||
|
||||
val averages = GradesAverages()
|
||||
var proposedGrade: GradeFull? = null
|
||||
var finalGrade: GradeFull? = null
|
||||
|
@ -6,7 +6,8 @@ package pl.szczodrzynski.edziennik.ui.modules.grades.viewholder
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter
|
||||
|
||||
interface BindableViewHolder<T> {
|
||||
fun onBind(activity: AppCompatActivity, app: App, item: T, position: Int)
|
||||
fun onBind(activity: AppCompatActivity, app: App, item: T, position: Int, adapter: GradesAdapter)
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.databinding.GradesItemEmptyBinding
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesEmpty
|
||||
|
||||
class EmptyViewHolder(
|
||||
@ -21,7 +22,7 @@ class EmptyViewHolder(
|
||||
private const val TAG = "EmptyViewHolder"
|
||||
}
|
||||
|
||||
override fun onBind(activity: AppCompatActivity, app: App, item: GradesEmpty, position: Int) {
|
||||
override fun onBind(activity: AppCompatActivity, app: App, item: GradesEmpty, position: Int, adapter: GradesAdapter) {
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -7,11 +7,14 @@ package pl.szczodrzynski.edziennik.ui.modules.grades.viewholder
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
||||
import pl.szczodrzynski.edziennik.databinding.GradesItemGradeBinding
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSubject
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class GradeViewHolder(
|
||||
@ -24,7 +27,7 @@ class GradeViewHolder(
|
||||
}
|
||||
|
||||
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
|
||||
override fun onBind(activity: AppCompatActivity, app: App, grade: GradeFull, position: Int) {
|
||||
override fun onBind(activity: AppCompatActivity, app: App, grade: GradeFull, position: Int, adapter: GradesAdapter) {
|
||||
val manager = app.gradesManager
|
||||
|
||||
b.gradeName.setGrade(grade, manager, bigView = true)
|
||||
@ -45,19 +48,35 @@ class GradeViewHolder(
|
||||
grade.category
|
||||
}
|
||||
|
||||
b.gradeWeight.text = manager.getWeightString(activity, grade, showClassAverage = true)
|
||||
val weightText = manager.getWeightString(activity, grade, showClassAverage = true)
|
||||
b.gradeWeight.text = weightText
|
||||
b.gradeWeight.isVisible = weightText != null
|
||||
|
||||
b.gradeTeacherName.text = grade.teacherFullName
|
||||
b.gradeAddedDate.text = Date.fromMillis(grade.addedDate).let {
|
||||
it.getRelativeString(app, 5) ?: it.formattedStringShort
|
||||
}
|
||||
|
||||
/*if (!grade.seen) {
|
||||
b.gradeDescription.setBackground(mContext.getResources().getDrawable(R.drawable.bg_rounded_4dp))
|
||||
b.gradeDescription.getBackground()
|
||||
.setColorFilter(PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY))
|
||||
} else {
|
||||
b.gradeDescription.setBackground(null)
|
||||
}*/
|
||||
b.unread.isVisible = grade.showAsUnseen
|
||||
if (!grade.seen) {
|
||||
manager.markAsSeen(grade)
|
||||
val subject = adapter.items.firstOrNull {
|
||||
it is GradesSubject && it.subjectId == grade.subjectId
|
||||
} as? GradesSubject ?: return
|
||||
|
||||
val semester = subject.semesters.firstOrNull { it.number == grade.semester } ?: return
|
||||
|
||||
semester.hasUnseen = semester.grades.any { !it.seen }
|
||||
// check if the unseen status has changed
|
||||
if (!semester.hasUnseen) {
|
||||
adapter.notifyItemChanged(semester)
|
||||
}
|
||||
if (!subject.hasUnseen) {
|
||||
adapter.notifyItemChanged(subject)
|
||||
}
|
||||
if (manager.hideImproved && grade.isImproved) {
|
||||
adapter.removeItem(grade)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ package pl.szczodrzynski.edziennik.ui.modules.grades.viewholder
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
@ -14,6 +15,7 @@ import pl.szczodrzynski.edziennik.databinding.GradesItemSemesterBinding
|
||||
import pl.szczodrzynski.edziennik.setText
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSemester
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSubject
|
||||
|
||||
class SemesterViewHolder(
|
||||
inflater: LayoutInflater,
|
||||
@ -24,7 +26,7 @@ class SemesterViewHolder(
|
||||
private const val TAG = "SemesterViewHolder"
|
||||
}
|
||||
|
||||
override fun onBind(activity: AppCompatActivity, app: App, item: GradesSemester, position: Int) {
|
||||
override fun onBind(activity: AppCompatActivity, app: App, item: GradesSemester, position: Int, adapter: GradesAdapter) {
|
||||
val manager = app.gradesManager
|
||||
b.semesterName.setText(R.string.grades_semester_format, item.number)
|
||||
b.dropdownIcon.rotation = when (item.state) {
|
||||
@ -32,6 +34,33 @@ class SemesterViewHolder(
|
||||
else -> 180f
|
||||
}
|
||||
|
||||
b.unread.isVisible = item.hasUnseen
|
||||
|
||||
var unseenChanged = false
|
||||
if (item.proposedGrade?.seen == false) {
|
||||
manager.markAsSeen(item.proposedGrade!!)
|
||||
unseenChanged = true
|
||||
}
|
||||
if (item.finalGrade?.seen == false) {
|
||||
manager.markAsSeen(item.finalGrade!!)
|
||||
unseenChanged = true
|
||||
}
|
||||
|
||||
if (unseenChanged) {
|
||||
val subject = adapter.items.firstOrNull {
|
||||
it is GradesSubject && it.subjectId == item.subjectId
|
||||
} as? GradesSubject ?: return
|
||||
|
||||
item.hasUnseen = item.grades.any { !it.seen }
|
||||
// check if the unseen status has changed
|
||||
if (!item.hasUnseen) {
|
||||
adapter.notifyItemChanged(item)
|
||||
}
|
||||
if (!subject.hasUnseen) {
|
||||
adapter.notifyItemChanged(subject)
|
||||
}
|
||||
}
|
||||
|
||||
b.average.text = manager.getAverageString(app, item.averages)
|
||||
b.proposedGrade.setGrade(item.proposedGrade, manager)
|
||||
b.finalGrade.setGrade(item.finalGrade, manager)
|
||||
|
@ -16,6 +16,7 @@ import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.databinding.GradesItemStatsBinding
|
||||
import pl.szczodrzynski.edziennik.onClick
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesStats
|
||||
import java.text.DecimalFormat
|
||||
|
||||
@ -28,7 +29,7 @@ class StatsViewHolder(
|
||||
private const val TAG = "StatsViewHolder"
|
||||
}
|
||||
|
||||
override fun onBind(activity: AppCompatActivity, app: App, item: GradesStats, position: Int) {
|
||||
override fun onBind(activity: AppCompatActivity, app: App, item: GradesStats, position: Int, adapter: GradesAdapter) {
|
||||
val manager = app.gradesManager
|
||||
val showAverages = mutableListOf<Int>()
|
||||
val showPoint = mutableListOf<Int>()
|
||||
@ -109,7 +110,7 @@ class StatsViewHolder(
|
||||
}
|
||||
|
||||
private fun getSemesterString(context: Context, expected: Float, proposed: Float, final: Float, notAllFinal: Boolean) : Pair<String?, String?> {
|
||||
val format = DecimalFormat("#.##")
|
||||
val format = DecimalFormat("#.00")
|
||||
|
||||
val average = when {
|
||||
final != 0f -> final
|
||||
|
@ -14,6 +14,7 @@ import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import androidx.core.view.get
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
@ -21,6 +22,7 @@ import pl.szczodrzynski.edziennik.databinding.GradesItemSubjectBinding
|
||||
import pl.szczodrzynski.edziennik.dp
|
||||
import pl.szczodrzynski.edziennik.setText
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.GradeView
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter.Companion.STATE_CLOSED
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSubject
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
@ -34,7 +36,7 @@ class SubjectViewHolder(
|
||||
private const val TAG = "SubjectViewHolder"
|
||||
}
|
||||
|
||||
override fun onBind(activity: AppCompatActivity, app: App, item: GradesSubject, position: Int) {
|
||||
override fun onBind(activity: AppCompatActivity, app: App, item: GradesSubject, position: Int, adapter: GradesAdapter) {
|
||||
val manager = app.gradesManager
|
||||
val contextWrapper = ContextThemeWrapper(activity, Themes.themeInt)
|
||||
|
||||
@ -44,6 +46,8 @@ class SubjectViewHolder(
|
||||
else -> 180f
|
||||
}
|
||||
|
||||
b.unread.isVisible = item.hasUnseen
|
||||
|
||||
b.previewContainer.visibility = if (item.state == STATE_CLOSED) View.VISIBLE else View.INVISIBLE
|
||||
b.yearSummary.visibility = if (item.state == STATE_CLOSED) View.INVISIBLE else View.VISIBLE
|
||||
|
||||
@ -71,7 +75,10 @@ class SubjectViewHolder(
|
||||
})
|
||||
}*/
|
||||
|
||||
val hideImproved = manager.hideImproved
|
||||
for (grade in firstSemester.grades) {
|
||||
if (hideImproved && grade.isImproved)
|
||||
continue
|
||||
b.gradesContainer.addView(GradeView(
|
||||
contextWrapper,
|
||||
grade,
|
||||
|
@ -71,7 +71,7 @@ class CounterActivity : AppCompatActivity(), CoroutineScope {
|
||||
bellSyncDiffMillis *= app.config.timetable.bellSyncMultiplier.toLong()
|
||||
}
|
||||
|
||||
counterJob = startCoroutineTimer(repeatMillis = 1000) {
|
||||
counterJob = startCoroutineTimer(repeatMillis = 500) {
|
||||
update()
|
||||
}
|
||||
}}
|
||||
|
@ -39,7 +39,7 @@ class HomeDummyCard(override val id: Int) : HomeCard, CoroutineScope {
|
||||
}
|
||||
holder.root += text
|
||||
|
||||
timer = startCoroutineTimer(repeatMillis = 1000) {
|
||||
timer = startCoroutineTimer(repeatMillis = 500) {
|
||||
time++
|
||||
text.text = "Coroutine timer at #$id! $time seconds"
|
||||
}
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.home.cards
|
||||
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.graphics.Typeface
|
||||
import android.os.Build
|
||||
import android.text.TextUtils
|
||||
@ -18,7 +16,6 @@ import android.widget.LinearLayout
|
||||
import android.widget.LinearLayout.HORIZONTAL
|
||||
import android.widget.LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
import android.widget.TextView
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.view.plusAssign
|
||||
import androidx.core.view.setMargins
|
||||
import androidx.lifecycle.Observer
|
||||
@ -28,23 +25,16 @@ import kotlinx.coroutines.Job
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_FINAL
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_PROPOSED
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_FINAL
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_PROPOSED
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_FINAL
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_PROPOSED
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Subject
|
||||
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
||||
import pl.szczodrzynski.edziennik.databinding.CardHomeGradesBinding
|
||||
import pl.szczodrzynski.edziennik.dp
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.GradeView
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCard
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
|
||||
import pl.szczodrzynski.edziennik.utils.Colors
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_DEFAULT
|
||||
import pl.szczodrzynski.edziennik.utils.models.ItemGradesSubjectModel
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
@ -127,32 +117,11 @@ class HomeGradesCard(
|
||||
16 /*ellipsize width*/)) / 1.5f
|
||||
|
||||
subject.grades1.onEach { grade ->
|
||||
val gradeColor = when (App.config.forProfile().grades.colorMode) {
|
||||
COLOR_MODE_DEFAULT -> grade.color
|
||||
else -> Colors.gradeToColor(grade)
|
||||
}
|
||||
|
||||
val gradeName = TextView(gradeItem.context).apply {
|
||||
text = when (grade.type) {
|
||||
TYPE_SEMESTER1_PROPOSED, TYPE_SEMESTER2_PROPOSED -> app.getString(R.string.grade_semester_proposed_format, grade.name)
|
||||
TYPE_SEMESTER1_FINAL, TYPE_SEMESTER2_FINAL -> app.getString(R.string.grade_semester_final_format, grade.name)
|
||||
TYPE_YEAR_PROPOSED -> app.getString(R.string.grade_year_proposed_format, grade.name)
|
||||
TYPE_YEAR_FINAL -> app.getString(R.string.grade_year_final_format, grade.name)
|
||||
else -> grade.name
|
||||
}
|
||||
|
||||
setTextColor(when (ColorUtils.calculateLuminance(gradeColor) > 0.25) {
|
||||
true -> 0xff000000
|
||||
else -> 0xffffffff
|
||||
}.toInt())
|
||||
|
||||
setTypeface(null, Typeface.BOLD)
|
||||
setBackgroundResource(R.drawable.bg_rounded_4dp)
|
||||
background.colorFilter = PorterDuffColorFilter(gradeColor, PorterDuff.Mode.MULTIPLY)
|
||||
setPadding(5.dp, 0, 5.dp, 0)
|
||||
|
||||
measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
}
|
||||
val gradeName = GradeView(
|
||||
gradeItem.context,
|
||||
grade,
|
||||
app.gradesManager
|
||||
)
|
||||
|
||||
totalWidth += gradeName.measuredWidth + 5.dp
|
||||
|
||||
|
@ -166,8 +166,8 @@ class HomeTimetableCard(
|
||||
&& !(it.isCancelled && ignoreCancelled)
|
||||
}
|
||||
|
||||
if (lessons.isEmpty() && timetableDate.weekDay <= 5)
|
||||
break
|
||||
//if (lessons.isEmpty() && timetableDate.weekDay <= 5)
|
||||
// break
|
||||
|
||||
checkedDays++
|
||||
}
|
||||
@ -249,7 +249,7 @@ class HomeTimetableCard(
|
||||
|
||||
subjectSpannable = firstLesson.subjectSpannable
|
||||
|
||||
counterJob = startCoroutineTimer(repeatMillis = 1000) {
|
||||
counterJob = startCoroutineTimer(repeatMillis = 500) {
|
||||
count()
|
||||
}
|
||||
}
|
||||
|
@ -523,10 +523,12 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
);
|
||||
}
|
||||
private String getSyncCardQuietHoursSubText() {
|
||||
if (app.getConfig().getSync().getQuietHoursStart() == null || app.getConfig().getSync().getQuietHoursEnd() == null)
|
||||
return "";
|
||||
return getString(
|
||||
app.getConfig().getSync().getQuietHoursStart() >= app.getConfig().getSync().getQuietHoursEnd() ? R.string.settings_sync_quiet_hours_subtext_next_day_format : R.string.settings_sync_quiet_hours_subtext_format,
|
||||
Time.fromMillis(Math.abs(app.getConfig().getSync().getQuietHoursStart())).getStringHM(),
|
||||
Time.fromMillis(app.getConfig().getSync().getQuietHoursEnd()).getStringHM()
|
||||
app.getConfig().getSync().getQuietHoursStart().getValue() >= app.getConfig().getSync().getQuietHoursEnd().getValue() ? R.string.settings_sync_quiet_hours_subtext_next_day_format : R.string.settings_sync_quiet_hours_subtext_format,
|
||||
app.getConfig().getSync().getQuietHoursStart().getStringHM(),
|
||||
app.getConfig().getSync().getQuietHoursEnd().getStringHM()
|
||||
);
|
||||
}
|
||||
private MaterialAboutItem getSyncCardWifiItem() {
|
||||
@ -650,7 +652,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
.size(IconicsSize.dp(iconSizeDp))
|
||||
.color(IconicsColor.colorInt(iconColor))
|
||||
);
|
||||
syncCardQuietHoursItem.setChecked(app.getConfig().getSync().getQuietHoursStart() > 0);
|
||||
syncCardQuietHoursItem.setChecked(app.getConfig().getSync().getQuietHoursEnabled());
|
||||
syncCardQuietHoursItem.setSubTextChecked(getSyncCardQuietHoursSubText());
|
||||
syncCardQuietHoursItem.setOnClickAction(() -> {
|
||||
new MaterialDialog.Builder(activity)
|
||||
@ -662,10 +664,12 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
.itemsCallback((dialog, itemView, position, text) -> {
|
||||
if (position == 0) {
|
||||
// set beginning
|
||||
Time time = Time.fromMillis(Math.abs(app.getConfig().getSync().getQuietHoursStart()));
|
||||
Time time = app.getConfig().getSync().getQuietHoursStart();
|
||||
if (time == null)
|
||||
time = new Time(22, 30, 0);
|
||||
TimePickerDialog.newInstance((v2, hourOfDay, minute, second) -> {
|
||||
// if it's disabled, it'll be enabled automatically
|
||||
app.getConfig().getSync().setQuietHoursStart(new Time(hourOfDay, minute, second).getInMillis());
|
||||
app.getConfig().getSync().setQuietHoursEnabled(true);
|
||||
app.getConfig().getSync().setQuietHoursStart(new Time(hourOfDay, minute, second));
|
||||
syncCardQuietHoursItem.setChecked(true);
|
||||
syncCardQuietHoursItem.setSubTextChecked(getSyncCardQuietHoursSubText());
|
||||
refreshMaterialAboutList();
|
||||
@ -673,13 +677,12 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
}
|
||||
else {
|
||||
// set end
|
||||
Time time = Time.fromMillis(app.getConfig().getSync().getQuietHoursEnd());
|
||||
Time time = app.getConfig().getSync().getQuietHoursEnd();
|
||||
if (time == null)
|
||||
time = new Time(5, 30, 0);
|
||||
TimePickerDialog.newInstance((v2, hourOfDay, minute, second) -> {
|
||||
if (app.getConfig().getSync().getQuietHoursStart() < 0) {
|
||||
// if it's disabled, enable
|
||||
app.getConfig().getSync().setQuietHoursStart(-1 * app.getConfig().getSync().getQuietHoursStart());
|
||||
}
|
||||
app.getConfig().getSync().setQuietHoursEnd(new Time(hourOfDay, minute, second).getInMillis());
|
||||
app.getConfig().getSync().setQuietHoursEnabled(true);
|
||||
app.getConfig().getSync().setQuietHoursEnd(new Time(hourOfDay, minute, second));
|
||||
syncCardQuietHoursItem.setChecked(true);
|
||||
syncCardQuietHoursItem.setSubTextChecked(getSyncCardQuietHoursSubText());
|
||||
refreshMaterialAboutList();
|
||||
@ -689,15 +692,10 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
.show();
|
||||
});
|
||||
syncCardQuietHoursItem.setOnChangeAction((isChecked, tag) -> {
|
||||
if (isChecked && app.getConfig().getSync().getQuietHoursStart() < 0) {
|
||||
app.getConfig().getSync().setQuietHoursStart(app.getConfig().getSync().getQuietHoursStart() * -1);
|
||||
}
|
||||
else if (!isChecked && app.getConfig().getSync().getQuietHoursStart() > 0) {
|
||||
app.getConfig().getSync().setQuietHoursStart(app.getConfig().getSync().getQuietHoursStart() * -1);
|
||||
}
|
||||
else if (isChecked && app.getConfig().getSync().getQuietHoursStart() == 0) {
|
||||
app.getConfig().getSync().setQuietHoursStart(new Time(22, 30, 0).getInMillis());
|
||||
app.getConfig().getSync().setQuietHoursEnd(new Time(5, 30, 0).getInMillis());
|
||||
app.getConfig().getSync().setQuietHoursEnabled(isChecked);
|
||||
if (isChecked && app.getConfig().getSync().getQuietHoursStart() == null) {
|
||||
app.getConfig().getSync().setQuietHoursStart(new Time(22, 30, 0));
|
||||
app.getConfig().getSync().setQuietHoursEnd(new Time(5, 30, 0));
|
||||
syncCardQuietHoursItem.setSubTextChecked(getSyncCardQuietHoursSubText());
|
||||
refreshMaterialAboutList();
|
||||
}
|
||||
|
@ -5,15 +5,15 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.timetable
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
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.setPadding
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import com.linkedin.android.tachyon.DayView
|
||||
import com.linkedin.android.tachyon.DayViewConfig
|
||||
@ -27,6 +27,7 @@ import pl.szczodrzynski.edziennik.data.db.full.LessonFull
|
||||
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.PagerFragment
|
||||
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
|
||||
@ -35,7 +36,7 @@ import java.util.*
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.math.min
|
||||
|
||||
class TimetableDayFragment : Fragment(), CoroutineScope {
|
||||
class TimetableDayFragment : PagerFragment(), CoroutineScope {
|
||||
companion object {
|
||||
private const val TAG = "TimetableDayFragment"
|
||||
}
|
||||
@ -44,7 +45,7 @@ class TimetableDayFragment : Fragment(), CoroutineScope {
|
||||
private lateinit var activity: MainActivity
|
||||
private lateinit var inflater: AsyncLayoutInflater
|
||||
|
||||
private lateinit var job: Job
|
||||
private val job: Job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
@ -53,7 +54,7 @@ class TimetableDayFragment : Fragment(), CoroutineScope {
|
||||
private var endHour = DEFAULT_END_HOUR
|
||||
private var firstEventMinute = 24 * 60
|
||||
|
||||
private val utils by lazy { TimetableUtils() }
|
||||
private val manager by lazy { app.timetableManager }
|
||||
|
||||
// find SwipeRefreshLayout in the hierarchy
|
||||
private val refreshLayout by lazy { view?.findParentById(R.id.refreshLayout) }
|
||||
@ -88,25 +89,23 @@ class TimetableDayFragment : Fragment(), CoroutineScope {
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
activity = (getActivity() as MainActivity?) ?: return null
|
||||
if (context == null)
|
||||
return null
|
||||
context ?: return null
|
||||
app = activity.application as App
|
||||
job = Job()
|
||||
this.inflater = AsyncLayoutInflater(context!!)
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
// TODO check if app, activity, b can be null
|
||||
if (app.profile == null || !isAdded)
|
||||
return
|
||||
|
||||
Log.d(TAG, "onViewCreated, date=$date")
|
||||
override fun onPageCreated(): Boolean {
|
||||
if (!isAdded)
|
||||
return false
|
||||
|
||||
// observe lesson database
|
||||
app.db.timetableDao().getForDate(App.profileId, date).observe(this, Observer { lessons ->
|
||||
@ -117,6 +116,8 @@ class TimetableDayFragment : Fragment(), CoroutineScope {
|
||||
processLessonList(lessons, events)
|
||||
}
|
||||
})
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun processLessonList(lessons: List<LessonFull>, events: List<EventFull>) {
|
||||
@ -174,6 +175,8 @@ class TimetableDayFragment : Fragment(), CoroutineScope {
|
||||
}
|
||||
dayView.setHourLabelViews(hourLabelViews)
|
||||
|
||||
lessons.forEach { it.showAsUnseen = !it.seen }
|
||||
|
||||
buildLessonViews(lessons.filter { it.type != Lesson.TYPE_NO_LESSONS }, events)
|
||||
}
|
||||
|
||||
@ -209,7 +212,6 @@ class TimetableDayFragment : Fragment(), CoroutineScope {
|
||||
eventView.tag = lesson
|
||||
|
||||
eventView.setOnClickListener {
|
||||
Log.d(TAG, "Clicked ${it.tag}")
|
||||
if (isAdded && it.tag is LessonFull)
|
||||
LessonDetailsDialog(activity, it.tag as LessonFull)
|
||||
}
|
||||
@ -275,10 +277,13 @@ class TimetableDayFragment : Fragment(), CoroutineScope {
|
||||
lb.detailsFirst.text = listOfNotEmpty(timeRange, classroomInfo).concat(bullet)
|
||||
lb.detailsSecond.text = listOfNotEmpty(teacherInfo, teamInfo).concat(bullet)
|
||||
|
||||
lb.unread = lesson.type != Lesson.TYPE_NORMAL && !lesson.seen
|
||||
lb.unread = lesson.type != Lesson.TYPE_NORMAL && lesson.showAsUnseen
|
||||
if (!lesson.seen) {
|
||||
manager.markAsSeen(lesson)
|
||||
}
|
||||
|
||||
//lb.subjectName.typeface = Typeface.create("sans-serif-light", Typeface.BOLD)
|
||||
lb.annotationVisible = utils.getAnnotation(activity, lesson, lb.annotation)
|
||||
lb.annotationVisible = manager.getAnnotation(activity, lesson, lb.annotation)
|
||||
|
||||
// The day view needs the event time ranges in the start minute/end minute format,
|
||||
// so calculate those here
|
||||
|
@ -15,7 +15,6 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import com.google.android.material.datepicker.MaterialDatePicker
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
@ -26,12 +25,9 @@ import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentTimetableV2Binding
|
||||
import pl.szczodrzynski.edziennik.observeOnce
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.timetable.GenerateBlockTimetableDialog
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
|
||||
@ -50,7 +46,7 @@ class TimetableFragment : Fragment(), CoroutineScope {
|
||||
private lateinit var activity: MainActivity
|
||||
private lateinit var b: FragmentTimetableV2Binding
|
||||
|
||||
private lateinit var job: Job
|
||||
private val job: Job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
@ -61,11 +57,6 @@ class TimetableFragment : Fragment(), CoroutineScope {
|
||||
activity = (getActivity() as MainActivity?) ?: return null
|
||||
context ?: return null
|
||||
app = activity.application as App
|
||||
job = Job()
|
||||
context!!.theme.applyStyle(Themes.appTheme, true)
|
||||
if (app.profile == null)
|
||||
return inflater.inflate(R.layout.fragment_loading, container, false)
|
||||
// activity, context and profile is valid
|
||||
b = FragmentTimetableV2Binding.inflate(inflater)
|
||||
// TODO: 2020-01-05 resolve issues with page scrolling (and scrolling up) with viewpager and swipe to refresh
|
||||
//b.refreshLayout.setParent(activity.swipeRefreshLayout)
|
||||
@ -91,8 +82,7 @@ class TimetableFragment : Fragment(), CoroutineScope {
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { launch {
|
||||
// TODO check if app, activity, b can be null
|
||||
if (app.profile == null || !isAdded)
|
||||
if (!isAdded)
|
||||
return@launch
|
||||
|
||||
if (app.profile.getStudentData("timetableNotPublic", false)) {
|
||||
@ -134,6 +124,8 @@ class TimetableFragment : Fragment(), CoroutineScope {
|
||||
endHour = lessonRanges.map { it.endTime.hour }.max()?.plus(1) ?: DEFAULT_END_HOUR
|
||||
}
|
||||
deferred.await()
|
||||
if (!isAdded)
|
||||
return@launch
|
||||
|
||||
val pagerAdapter = TimetablePagerAdapter(
|
||||
fragmentManager ?: return@launch,
|
||||
@ -141,7 +133,7 @@ class TimetableFragment : Fragment(), CoroutineScope {
|
||||
startHour,
|
||||
endHour
|
||||
)
|
||||
b.viewPager.offscreenPageLimit = 2
|
||||
b.viewPager.offscreenPageLimit = 1
|
||||
b.viewPager.adapter = pagerAdapter
|
||||
b.viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
|
||||
override fun onPageScrollStateChanged(state: Int) {
|
||||
@ -162,7 +154,7 @@ class TimetableFragment : Fragment(), CoroutineScope {
|
||||
activity.gainAttentionFAB()
|
||||
fabShown = true
|
||||
}
|
||||
markLessonsAsSeen()
|
||||
//markLessonsAsSeen()
|
||||
}
|
||||
})
|
||||
|
||||
@ -224,7 +216,7 @@ class TimetableFragment : Fragment(), CoroutineScope {
|
||||
})
|
||||
}}
|
||||
|
||||
private fun markLessonsAsSeen() = pageSelection?.let { date ->
|
||||
/*private fun markLessonsAsSeen() = pageSelection?.let { date ->
|
||||
app.db.timetableDao().getForDate(App.profileId, date).observeOnce(this@TimetableFragment, Observer { lessons ->
|
||||
lessons.forEach { lesson ->
|
||||
if (lesson.type != Lesson.TYPE_NORMAL && lesson.type != Lesson.TYPE_NO_LESSONS
|
||||
@ -233,5 +225,5 @@ class TimetableFragment : Fragment(), CoroutineScope {
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
@ -33,11 +33,6 @@ class TimetablePagerAdapter(
|
||||
putInt("endHour", endHour)
|
||||
}
|
||||
}
|
||||
/*return TimetableDayFragment().apply {
|
||||
arguments = Bundle().also {
|
||||
it.putLong("date", items[position].value.toLong())
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
override fun getCount(): Int {
|
||||
|
@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-2-23.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.views
|
||||
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.util.AttributeSet
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.Observer
|
||||
import com.google.android.material.datepicker.MaterialDatePicker
|
||||
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.full.LessonFull
|
||||
import pl.szczodrzynski.edziennik.observeOnce
|
||||
import pl.szczodrzynski.edziennik.utils.TextInputDropDown
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||
|
||||
class DateDropdown : 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)
|
||||
|
||||
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 showWeekDays = false
|
||||
var showDays = true
|
||||
var showOtherDate = true
|
||||
var nextLessonSubjectId: Long? = null
|
||||
var nextLessonSubjectName: String? = null
|
||||
var nextLessonTeamId: Long? = null
|
||||
var onDateSelected: ((date: Date, lesson: LessonFull?) -> Unit)? = null
|
||||
var onWeekDaySelected: ((weekDay: Int) -> Unit)? = null
|
||||
|
||||
override fun create(context: Context) {
|
||||
super.create(context)
|
||||
isEnabled = false
|
||||
}
|
||||
|
||||
suspend fun loadItems() {
|
||||
val date = Date.getToday()
|
||||
val today = date.value
|
||||
var weekDay = date.weekDay
|
||||
|
||||
val dates = withContext(Dispatchers.Default) {
|
||||
val dates = mutableListOf<Item>()
|
||||
|
||||
nextLessonSubjectId?.let {
|
||||
// item choosing the next lesson of specific subject - relative to selected date
|
||||
dates += Item(
|
||||
-it,
|
||||
context.getString(R.string.dialog_event_manual_date_next_lesson, nextLessonSubjectName),
|
||||
tag = nextLessonSubjectName
|
||||
)
|
||||
}
|
||||
|
||||
if (showWeekDays) {
|
||||
for (i in Week.MONDAY..Week.SUNDAY) {
|
||||
dates += Item(
|
||||
i.toLong(),
|
||||
Week.getFullDayName(i),
|
||||
tag = i
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (showDays) {
|
||||
// TODAY
|
||||
dates += Item(
|
||||
date.value.toLong(),
|
||||
context.getString(R.string.dialog_event_manual_date_today, date.formattedString),
|
||||
tag = date.clone()
|
||||
)
|
||||
|
||||
// TOMORROW
|
||||
if (weekDay < 4) {
|
||||
date.stepForward(0, 0, 1)
|
||||
weekDay++
|
||||
dates += Item(
|
||||
date.value.toLong(),
|
||||
context.getString(R.string.dialog_event_manual_date_tomorrow, date.formattedString),
|
||||
tag = date.clone()
|
||||
)
|
||||
}
|
||||
// REMAINING SCHOOL DAYS OF THE CURRENT WEEK
|
||||
while (weekDay < 4) {
|
||||
date.stepForward(0, 0, 1) // step one day forward
|
||||
weekDay++
|
||||
dates += Item(
|
||||
date.value.toLong(),
|
||||
context.getString(R.string.dialog_event_manual_date_this_week, Week.getFullDayName(weekDay), date.formattedString),
|
||||
tag = date.clone()
|
||||
)
|
||||
}
|
||||
// go to next week Monday
|
||||
date.stepForward(0, 0, -weekDay + 7)
|
||||
weekDay = 0
|
||||
// ALL SCHOOL DAYS OF THE NEXT WEEK
|
||||
while (weekDay < 4) {
|
||||
dates += Item(
|
||||
date.value.toLong(),
|
||||
context.getString(R.string.dialog_event_manual_date_next_week, Week.getFullDayName(weekDay), date.formattedString),
|
||||
tag = date.clone()
|
||||
)
|
||||
date.stepForward(0, 0, 1) // step one day forward
|
||||
weekDay++
|
||||
}
|
||||
}
|
||||
|
||||
if (showOtherDate) {
|
||||
dates += Item(
|
||||
-1L,
|
||||
context.getString(R.string.dialog_event_manual_date_other),
|
||||
tag = -1L
|
||||
)
|
||||
}
|
||||
dates
|
||||
}
|
||||
|
||||
clear().append(dates)
|
||||
isEnabled = true
|
||||
|
||||
setOnChangeListener { item ->
|
||||
when (item.tag) {
|
||||
-1L -> {
|
||||
pickerDialog()
|
||||
false
|
||||
}
|
||||
is Date -> {
|
||||
onDateSelected?.invoke(item.tag, null)
|
||||
true
|
||||
}
|
||||
is Int -> {
|
||||
onWeekDaySelected?.invoke(item.tag)
|
||||
true
|
||||
}
|
||||
is String -> {
|
||||
/* next lesson of subject */
|
||||
activity ?: return@setOnChangeListener false
|
||||
val subjectId = -item.id
|
||||
val startDate = getSelected() as? Date ?: Date.getToday()
|
||||
when (nextLessonTeamId) {
|
||||
null -> db.timetableDao().getNextWithSubject(profileId, startDate, subjectId)
|
||||
else -> db.timetableDao().getNextWithSubjectAndTeam(profileId, startDate, subjectId, nextLessonTeamId ?: -1)
|
||||
}.observeOnce(activity!!, Observer {
|
||||
if (it == null) {
|
||||
Toast.makeText(context, R.string.dropdown_date_no_more_lessons, Toast.LENGTH_LONG).show()
|
||||
return@Observer
|
||||
}
|
||||
val lessonDate = it.displayDate ?: return@Observer
|
||||
selectDate(lessonDate)
|
||||
onDateSelected?.invoke(lessonDate, it)
|
||||
})
|
||||
false
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun pickerDialog() {
|
||||
MaterialDatePicker.Builder
|
||||
.datePicker()
|
||||
.setSelection(
|
||||
if (selected?.tag is Date)
|
||||
(selected?.tag as Date).inMillis
|
||||
else
|
||||
Date.getToday().inMillis
|
||||
)
|
||||
.build()
|
||||
.apply {
|
||||
addOnPositiveButtonClickListener {
|
||||
val dateSelected = Date.fromMillis(it)
|
||||
selectDate(dateSelected)
|
||||
onDateSelected?.invoke(dateSelected, null)
|
||||
}
|
||||
this@DateDropdown.activity ?: return@apply
|
||||
show(this@DateDropdown.activity!!.supportFragmentManager, "MaterialDatePicker")
|
||||
}
|
||||
}
|
||||
|
||||
fun selectDate(date: Date) {
|
||||
if (select(date) == null)
|
||||
select(Item(
|
||||
date.value.toLong(),
|
||||
date.formattedString,
|
||||
tag = date
|
||||
))
|
||||
}
|
||||
fun selectWeekDay(weekDay: Int) {
|
||||
if (select(tag = weekDay) == null)
|
||||
select(Item(
|
||||
weekDay.toLong(),
|
||||
Week.getFullDayName(weekDay),
|
||||
tag = weekDay
|
||||
))
|
||||
}
|
||||
|
||||
fun selectDefault(date: Date?) {
|
||||
if (date == null || selected != null)
|
||||
return
|
||||
selectDate(date)
|
||||
}
|
||||
fun selectDefault(weekDay: Int?) {
|
||||
if (weekDay == null || selected != null)
|
||||
return
|
||||
selectWeekDay(weekDay)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently selected date.
|
||||
* ### Returns:
|
||||
* - null if no valid date is selected
|
||||
* - [Date] - the selected date, if [showDays] or [showOtherDate] == true
|
||||
* - [Int] - the selected week day, if [showWeekDays] == true
|
||||
*/
|
||||
fun getSelected(): Any? {
|
||||
return when (val tag = selected?.tag) {
|
||||
is Date -> tag
|
||||
is Int -> tag
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-2-23.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.views
|
||||
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.text.InputType
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
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.utils.TextInputDropDown
|
||||
|
||||
class SubjectDropdown : 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)
|
||||
|
||||
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 showNoSubject = true
|
||||
var showCustomSubject = false
|
||||
var customSubjectName = ""
|
||||
var onSubjectSelected: ((subjectId: Long?) -> Unit)? = null
|
||||
var onCustomSubjectSelected: ((subjectName: String) -> Unit)? = null
|
||||
|
||||
override fun create(context: Context) {
|
||||
super.create(context)
|
||||
isEnabled = false
|
||||
}
|
||||
|
||||
suspend fun loadItems() {
|
||||
val subjects = withContext(Dispatchers.Default) {
|
||||
val list = mutableListOf<Item>()
|
||||
|
||||
if (showNoSubject) {
|
||||
list += Item(
|
||||
-1L,
|
||||
context.getString(R.string.dialog_event_manual_no_subject),
|
||||
tag = -1L
|
||||
)
|
||||
}
|
||||
|
||||
if (showCustomSubject) {
|
||||
list += Item(
|
||||
-2L,
|
||||
context.getString(R.string.dropdown_subject_custom),
|
||||
tag = -2L
|
||||
)
|
||||
}
|
||||
|
||||
val subjects = db.subjectDao().getAllNow(profileId)
|
||||
|
||||
list += subjects.map { Item(
|
||||
it.id,
|
||||
it.longName,
|
||||
tag = it.id
|
||||
) }
|
||||
|
||||
list
|
||||
}
|
||||
|
||||
clear().append(subjects)
|
||||
isEnabled = true
|
||||
|
||||
setOnChangeListener {
|
||||
when (it.tag) {
|
||||
-2L -> {
|
||||
// custom subject
|
||||
customNameDialog()
|
||||
false
|
||||
}
|
||||
-1L -> {
|
||||
// no subject
|
||||
onSubjectSelected?.invoke(null)
|
||||
true
|
||||
}
|
||||
is Long -> {
|
||||
// selected a subject
|
||||
onSubjectSelected?.invoke(it.tag)
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun customNameDialog() {
|
||||
activity ?: return
|
||||
MaterialDialog.Builder(activity!!)
|
||||
.title("Własny przedmiot")
|
||||
.inputType(InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE)
|
||||
.input("Nazwa", "") { _: MaterialDialog?, input: CharSequence ->
|
||||
customSubjectName = input.toString()
|
||||
select(Item(
|
||||
-1L * customSubjectName.crc16(),
|
||||
customSubjectName,
|
||||
tag = customSubjectName
|
||||
))
|
||||
onCustomSubjectSelected?.invoke(customSubjectName)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
fun selectSubject(subjectId: Long) {
|
||||
if (select(subjectId) == null)
|
||||
select(Item(
|
||||
subjectId,
|
||||
"nieznany przedmiot ($subjectId)",
|
||||
tag = subjectId
|
||||
))
|
||||
}
|
||||
|
||||
fun selectDefault(subjectId: Long?) {
|
||||
if (subjectId == null || selected != null)
|
||||
return
|
||||
selectSubject(subjectId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently selected subject.
|
||||
* ### Returns:
|
||||
* - null if no valid subject is selected
|
||||
* - [Long] - the selected subject's ID
|
||||
* - [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 String -> selected?.tag as String
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-2-23.
|
||||
*/
|
||||
|
||||
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.utils.TextInputDropDown
|
||||
|
||||
class TeacherDropdown : 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)
|
||||
|
||||
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
|
||||
|
||||
override fun create(context: Context) {
|
||||
super.create(context)
|
||||
isEnabled = false
|
||||
}
|
||||
|
||||
suspend fun loadItems() {
|
||||
val teachers = withContext(Dispatchers.Default) {
|
||||
val list = mutableListOf<Item>()
|
||||
|
||||
if (showNoTeacher) {
|
||||
list += Item(
|
||||
-1L,
|
||||
context.getString(R.string.dialog_event_manual_no_teacher),
|
||||
tag = -1L
|
||||
)
|
||||
}
|
||||
|
||||
val teachers = db.teacherDao().getAllNow(profileId)
|
||||
|
||||
list += teachers.map { Item(
|
||||
it.id,
|
||||
it.fullName,
|
||||
tag = it.id
|
||||
) }
|
||||
|
||||
list
|
||||
}
|
||||
|
||||
clear().append(teachers)
|
||||
isEnabled = true
|
||||
|
||||
setOnChangeListener {
|
||||
when (it.tag) {
|
||||
-1L -> {
|
||||
// no teacher
|
||||
onTeacherSelected?.invoke(null)
|
||||
true
|
||||
}
|
||||
is Long -> {
|
||||
// selected a teacher
|
||||
onTeacherSelected?.invoke(it.tag)
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun selectTeacher(teacherId: Long) {
|
||||
if (select(teacherId) == null)
|
||||
select(Item(
|
||||
teacherId,
|
||||
"nieznany nauczyciel ($teacherId)",
|
||||
tag = teacherId
|
||||
))
|
||||
}
|
||||
|
||||
fun selectDefault(teacherId: Long?) {
|
||||
if (teacherId == null || selected != null)
|
||||
return
|
||||
selectTeacher(teacherId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently selected teacher.
|
||||
* ### Returns:
|
||||
* - null if no valid teacher is selected
|
||||
* - [Long] - the selected teacher's ID
|
||||
*/
|
||||
fun getSelected(): Long? {
|
||||
return when (selected?.tag) {
|
||||
-1L -> null
|
||||
is Long -> selected?.tag as Long
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-3-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
|
||||
import pl.szczodrzynski.edziennik.data.db.AppDb
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Team
|
||||
import pl.szczodrzynski.edziennik.utils.TextInputDropDown
|
||||
|
||||
class TeamDropdown : 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)
|
||||
|
||||
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
|
||||
|
||||
override fun create(context: Context) {
|
||||
super.create(context)
|
||||
isEnabled = false
|
||||
}
|
||||
|
||||
suspend fun loadItems() {
|
||||
val teams = withContext(Dispatchers.Default) {
|
||||
val list = mutableListOf<Item>()
|
||||
|
||||
if (showNoTeam) {
|
||||
list += Item(
|
||||
-1L,
|
||||
context.getString(R.string.dialog_event_manual_no_team),
|
||||
tag = -1L
|
||||
)
|
||||
}
|
||||
|
||||
val teams = db.teamDao().getAllNow(profileId)
|
||||
|
||||
list += teams.map { Item(
|
||||
it.id,
|
||||
it.name,
|
||||
tag = it.id
|
||||
) }
|
||||
|
||||
list
|
||||
}
|
||||
|
||||
clear().append(teams)
|
||||
isEnabled = true
|
||||
|
||||
setOnChangeListener {
|
||||
when (it.tag) {
|
||||
-1L -> {
|
||||
// no team
|
||||
onTeamSelected?.invoke(null)
|
||||
true
|
||||
}
|
||||
is Long -> {
|
||||
// selected a team
|
||||
onTeamSelected?.invoke(it.tag)
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun selectTeam(teamId: Long) {
|
||||
if (select(teamId) == null)
|
||||
select(Item(
|
||||
teamId,
|
||||
"nieznana grupa ($teamId)",
|
||||
tag = teamId
|
||||
))
|
||||
}
|
||||
|
||||
fun selectDefault(teamId: Long?) {
|
||||
if (teamId == null || selected != null)
|
||||
return
|
||||
selectTeam(teamId)
|
||||
}
|
||||
|
||||
fun selectTeamClass() {
|
||||
select(items.singleOrNull {
|
||||
it.tag is Team && it.tag.type == Team.TYPE_CLASS
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently selected team.
|
||||
* ### Returns:
|
||||
* - null if no valid team is selected
|
||||
* - [Long] - the team's ID
|
||||
*/
|
||||
fun getSelected(): Any? {
|
||||
return when (selected?.tag) {
|
||||
-1L -> null
|
||||
is Long -> selected?.tag as Long
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-2-23.
|
||||
*/
|
||||
|
||||
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.*
|
||||
import pl.szczodrzynski.edziennik.data.db.AppDb
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LessonRange
|
||||
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
|
||||
import pl.szczodrzynski.edziennik.utils.TextInputDropDown
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
class TimeDropdown : TextInputDropDown {
|
||||
companion object {
|
||||
const val DISPLAY_LESSON_RANGES = 0
|
||||
const val DISPLAY_LESSONS = 1
|
||||
}
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
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 showAllDay = false
|
||||
var showCustomTime = true
|
||||
var displayMode = DISPLAY_LESSON_RANGES
|
||||
var lessonsDate: Date? = null
|
||||
var onTimeSelected: ((startTime: Time?, endTime: Time?, lessonNumber: Int?) -> Unit)? = null
|
||||
var onLessonSelected: ((lesson: LessonFull) -> Unit)? = null
|
||||
|
||||
override fun create(context: Context) {
|
||||
super.create(context)
|
||||
isEnabled = false
|
||||
}
|
||||
|
||||
suspend fun loadItems(): Boolean {
|
||||
var noTimetable = false
|
||||
val hours = withContext(Dispatchers.Default) {
|
||||
val hours = mutableListOf<Item>()
|
||||
|
||||
if (showAllDay) {
|
||||
hours += Item(
|
||||
0L,
|
||||
context.getString(R.string.dialog_event_manual_all_day),
|
||||
tag = 0L
|
||||
)
|
||||
}
|
||||
|
||||
if (showCustomTime) {
|
||||
hours += Item(
|
||||
-1,
|
||||
context.getString(R.string.dialog_event_manual_custom_time),
|
||||
tag = -1L
|
||||
)
|
||||
}
|
||||
|
||||
if (displayMode == DISPLAY_LESSON_RANGES) {
|
||||
val lessonRanges = db.lessonRangeDao().getAllNow(profileId)
|
||||
|
||||
hours += lessonRanges.map { Item(
|
||||
it.startTime.value.toLong(),
|
||||
context.getString(R.string.timetable_manual_dialog_time_format, it.startTime.stringHM, it.lessonNumber),
|
||||
tag = it
|
||||
) }
|
||||
}
|
||||
else if (displayMode == DISPLAY_LESSONS && lessonsDate != null) {
|
||||
val lessons = db.timetableDao().getForDateNow(profileId, lessonsDate!!)
|
||||
|
||||
if (lessons.isEmpty()) {
|
||||
hours += Item(
|
||||
-2L,
|
||||
context.getString(R.string.dialog_event_manual_no_timetable),
|
||||
tag = -2L
|
||||
)
|
||||
noTimetable = true
|
||||
return@withContext hours
|
||||
}
|
||||
|
||||
hours += lessons.map { lesson ->
|
||||
if (lesson.type == Lesson.TYPE_NO_LESSONS) {
|
||||
// indicate there are no lessons this day
|
||||
return@map Item(
|
||||
-2L,
|
||||
context.getString(R.string.dialog_event_manual_no_lessons),
|
||||
tag = -2L
|
||||
)
|
||||
}
|
||||
// create the lesson caption
|
||||
val text = listOfNotEmpty(
|
||||
lesson.displayStartTime?.stringHM ?: "",
|
||||
if (lesson.displaySubjectName != null) "-" else "",
|
||||
lesson.displaySubjectName?.let {
|
||||
when {
|
||||
lesson.type == Lesson.TYPE_CANCELLED
|
||||
|| lesson.type == Lesson.TYPE_SHIFTED_SOURCE -> it.asStrikethroughSpannable()
|
||||
lesson.type != Lesson.TYPE_NORMAL -> it.asItalicSpannable()
|
||||
else -> it
|
||||
}
|
||||
} ?: ""
|
||||
)
|
||||
// add an item with LessonFull as the tag
|
||||
return@map Item(
|
||||
lesson.displayStartTime?.value?.toLong() ?: -1,
|
||||
text.concat(" "),
|
||||
tag = lesson
|
||||
)
|
||||
}
|
||||
}
|
||||
hours
|
||||
}
|
||||
|
||||
clear().append(hours)
|
||||
isEnabled = true
|
||||
|
||||
setOnChangeListener {
|
||||
when (it.tag) {
|
||||
-2L -> {
|
||||
// no lessons this day
|
||||
deselect()
|
||||
false
|
||||
}
|
||||
-1L -> {
|
||||
// custom start hour
|
||||
pickerDialog()
|
||||
false
|
||||
}
|
||||
0L -> {
|
||||
// selected all day
|
||||
onTimeSelected?.invoke(null, null, null)
|
||||
true
|
||||
}
|
||||
is LessonFull -> {
|
||||
// selected a specific lesson
|
||||
onLessonSelected?.invoke(it.tag)
|
||||
true
|
||||
}
|
||||
is LessonRange -> {
|
||||
// selected a lesson range
|
||||
onTimeSelected?.invoke(it.tag.startTime, it.tag.endTime, it.tag.lessonNumber)
|
||||
true
|
||||
}
|
||||
is Time -> {
|
||||
// selected a time
|
||||
onTimeSelected?.invoke(it.tag, null, null)
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
return !noTimetable
|
||||
}
|
||||
|
||||
fun pickerDialog() {
|
||||
/*MaterialDatePicker.Builder
|
||||
.datePicker()
|
||||
.setSelection((selectedId?.let { Date.fromValue(it.toInt()) }
|
||||
?: Date.getToday()).inMillis)
|
||||
.build()
|
||||
.apply {
|
||||
addOnPositiveButtonClickListener {
|
||||
val dateSelected = Date.fromMillis(it)
|
||||
selectDate(dateSelected)
|
||||
}
|
||||
this@DateDropdown.activity ?: return@apply
|
||||
show(this@DateDropdown.activity!!.supportFragmentManager, "MaterialDatePicker")
|
||||
}*/
|
||||
}
|
||||
|
||||
fun selectTime(time: Time) {
|
||||
if (select(time.value.toLong()) == null)
|
||||
select(Item(
|
||||
time.value.toLong(),
|
||||
time.stringHM,
|
||||
tag = time
|
||||
))
|
||||
}
|
||||
|
||||
fun selectDefault(time: Time?) {
|
||||
if (time == null || selected != null)
|
||||
return
|
||||
selectTime(time)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently selected time.
|
||||
* ### Returns:
|
||||
* - null if no valid time is selected
|
||||
* - a [Pair] of [Time] and [Time]? - the selected time object, if [displayMode] == [DISPLAY_LESSONS] or [showCustomTime]
|
||||
* - [LessonRange] - the selected lesson range object, if [displayMode] == [DISPLAY_LESSON_RANGES]
|
||||
*/
|
||||
fun getSelected(): Any? {
|
||||
return when (val tag = selected?.tag) {
|
||||
0L -> null
|
||||
is LessonFull ->
|
||||
if (tag.displayStartTime != null)
|
||||
tag.displayStartTime!! to tag.displayEndTime
|
||||
else
|
||||
null
|
||||
is LessonRange -> tag
|
||||
is Time -> tag to null
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
@ -229,8 +229,8 @@ class WidgetTimetableProvider : AppWidgetProvider() {
|
||||
&& !(it.isCancelled && ignoreCancelled)
|
||||
}
|
||||
|
||||
if (lessons.isEmpty() && timetableDate.weekDay <= 5)
|
||||
break
|
||||
//if (lessons.isEmpty() && timetableDate.weekDay <= 5)
|
||||
// break
|
||||
|
||||
checkedDays++
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import androidx.core.graphics.drawable.DrawableCompat
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
|
||||
class TextInputDropDown : TextInputEditText {
|
||||
open class TextInputDropDown : TextInputEditText {
|
||||
constructor(context: Context) : super(context) {
|
||||
create(context)
|
||||
}
|
||||
@ -31,7 +31,7 @@ class TextInputDropDown : TextInputEditText {
|
||||
setText(selected?.displayText ?: selected?.text)
|
||||
}
|
||||
|
||||
fun create(context: Context) {
|
||||
open fun create(context: Context) {
|
||||
val drawable = context.resources.getDrawable(R.drawable.dropdown_arrow)
|
||||
val wrappedDrawable = DrawableCompat.wrap(drawable)
|
||||
DrawableCompat.setTint(wrappedDrawable, Themes.getPrimaryTextColor(context))
|
||||
|
@ -5,17 +5,23 @@
|
||||
package pl.szczodrzynski.edziennik.utils.managers
|
||||
|
||||
import android.content.Context
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_NORMAL
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_POINT_AVG
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_POINT_SUM
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_FINAL
|
||||
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesAverages
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSemester
|
||||
import pl.szczodrzynski.edziennik.utils.Colors
|
||||
import java.text.DecimalFormat
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.math.floor
|
||||
|
||||
class GradesManager(val app: App) {
|
||||
class GradesManager(val app: App) : CoroutineScope {
|
||||
companion object {
|
||||
const val ORDER_BY_DATE_DESC = 0
|
||||
const val ORDER_BY_SUBJECT_ASC = 1
|
||||
@ -30,7 +36,11 @@ class GradesManager(val app: App) {
|
||||
const val COLOR_MODE_WEIGHTED = 1
|
||||
}
|
||||
|
||||
private val gradeRegex by lazy { """([0-6])([+-])?""".toRegex() }
|
||||
private val job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Default
|
||||
|
||||
private val gradeRegex by lazy { """([+-])?([0-6])([+-])?""".toRegex() }
|
||||
private val format = DecimalFormat("#.##")
|
||||
|
||||
val orderBy
|
||||
@ -45,6 +55,10 @@ class GradesManager(val app: App) {
|
||||
get() = app.config.forProfile().grades.minusValue
|
||||
val dontCountGrades
|
||||
get() = app.config.forProfile().grades.dontCountGrades
|
||||
val hideImproved
|
||||
get() = app.config.forProfile().grades.hideImproved
|
||||
val averageWithoutWeight
|
||||
get() = app.config.forProfile().grades.averageWithoutWeight
|
||||
|
||||
|
||||
fun getOrderByString() = when (orderBy) {
|
||||
@ -66,51 +80,21 @@ class GradesManager(val app: App) {
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun gradeNameToColorStr(grade: String): String? {
|
||||
when (grade.toLowerCase()) {
|
||||
"+", "++", "+++" ->
|
||||
return "4caf50"
|
||||
"-", "-,", "-,-,", "np", "np.", "npnp", "np,", "np,np,", "bs", "nk" ->
|
||||
return "ff7043"
|
||||
"1-", "1", "f" ->
|
||||
return "ff0000"
|
||||
"1+", "ef" ->
|
||||
return "ff3d00"
|
||||
"2-", "2", "e" ->
|
||||
return "ff9100"
|
||||
"2+", "de" ->
|
||||
return "ffab00"
|
||||
"3-", "3", "d" ->
|
||||
return "ffff00"
|
||||
"3+", "cd" ->
|
||||
return "c6ff00"
|
||||
"4-", "4", "c" ->
|
||||
return "76ff03"
|
||||
"4+", "bc" ->
|
||||
return "64dd17"
|
||||
"5-", "5", "b" ->
|
||||
return "00c853"
|
||||
"5+", "ab" ->
|
||||
return "00bfa5"
|
||||
"6-", "6", "a" ->
|
||||
return "2196f3"
|
||||
"6+", "a+" ->
|
||||
return "0091ea"
|
||||
}
|
||||
return "bdbdbd"
|
||||
}
|
||||
|
||||
fun getRoundedGrade(value: Float): Int {
|
||||
return floor(value.toDouble()).toInt() + if (value % 1.0f >= 0.75) 1 else 0
|
||||
}
|
||||
|
||||
fun getGradeValue(grade: Grade): Float {
|
||||
gradeRegex.find(grade.name)?.let {
|
||||
var value = it[1].toFloatOrNull() ?: return grade.value
|
||||
if (it[2] == "+")
|
||||
value += plusValue ?: return grade.value
|
||||
if (it[2] == "-")
|
||||
value -= minusValue ?: return grade.value
|
||||
if (plusValue == null && minusValue == null)
|
||||
return grade.value
|
||||
|
||||
gradeRegex.find(grade.name)?.let { it ->
|
||||
var value = it[2].toFloatOrNull() ?: return grade.value
|
||||
when (it[1].notEmptyOrNull() ?: it[3]) {
|
||||
"+" -> value += plusValue ?: return grade.value
|
||||
"-" -> value -= minusValue ?: return grade.value
|
||||
else -> return grade.value
|
||||
}
|
||||
return value
|
||||
}
|
||||
return grade.value
|
||||
@ -122,8 +106,62 @@ class GradesManager(val app: App) {
|
||||
return grade.weight
|
||||
}
|
||||
|
||||
fun getColor(grade: Grade): Int {
|
||||
return Colors.gradeToColor(grade)
|
||||
fun getGradeColor(grade: Grade): Int {
|
||||
val type = grade.type
|
||||
val defColor = colorMode == COLOR_MODE_DEFAULT
|
||||
val valueMax = grade.valueMax ?: 0f
|
||||
|
||||
val color = when {
|
||||
type == TYPE_POINT_SUM && !defColor -> {
|
||||
when {
|
||||
grade.id < 0 -> grade.color and 0xffffff /* starting points */
|
||||
grade.value < 0 -> 0xf44336
|
||||
grade.value > 0 -> 0x4caf50
|
||||
else -> 0xbdbdbd
|
||||
}
|
||||
}
|
||||
type == TYPE_POINT_AVG && !defColor ->
|
||||
when (valueMax) {
|
||||
0f -> 0xbdbdbd
|
||||
else -> when (grade.value / valueMax * 100f) {
|
||||
in 0f..29f -> 0xf50000 // 1
|
||||
in 30f..49f -> 0xff5722 // 2
|
||||
in 50f..74f -> 0xff9100 // 3
|
||||
in 75f..89f -> 0xffd600 // 4
|
||||
in 90f..97f -> 0x00c853 // 5
|
||||
else -> 0x0091ea // 6
|
||||
}
|
||||
}
|
||||
type == TYPE_NORMAL && defColor -> grade.color and 0xffffff
|
||||
type in TYPE_NORMAL..TYPE_YEAR_FINAL -> {
|
||||
when (grade.name.toLowerCase()) {
|
||||
"+", "++", "+++" -> 0x4caf50
|
||||
"0", "-", "-,", "-,-,", "np", "np.", "npnp", "np,", "np,np,", "bs", "nk", "bz" -> 0xff7043
|
||||
"1-", "1", "f", "ng" -> 0xff0000
|
||||
"1+", "ef" -> 0xff3d00
|
||||
"2-", "2", "e", "ndp" -> 0xff9100
|
||||
"2+", "de" -> 0xffab00
|
||||
"3-", "3", "d", "popr" -> 0xffff00
|
||||
"3+", "cd" -> 0xc6ff00
|
||||
"4-", "4", "c", "db" -> 0x76ff03
|
||||
"4+", "bc" -> 0x64dd17
|
||||
"5-", "5", "b", "bdb" -> 0x00c853
|
||||
"5+", "ab" -> 0x00bfa5
|
||||
"6-", "6", "a", "wz" -> 0x2196f3
|
||||
"6+", "a+" -> 0x0091ea
|
||||
else -> grade.color and 0xffffff
|
||||
}
|
||||
}
|
||||
else -> grade.color and 0xffffff
|
||||
}
|
||||
return color or 0xff000000.toInt()
|
||||
}
|
||||
|
||||
fun markAsSeen(grade: GradeFull) {
|
||||
grade.seen = true
|
||||
startCoroutineTimer(500L, 0L) {
|
||||
app.db.metadataDao().setSeen(grade.profileId, grade, true)
|
||||
}
|
||||
}
|
||||
|
||||
fun calculateAverages(averages: GradesAverages, semesters: List<GradesSemester>? = null) {
|
||||
@ -142,7 +180,7 @@ class GradesManager(val app: App) {
|
||||
averages.normalWeightedCount > 0f -> {
|
||||
averages.normalWeightedSum / averages.normalWeightedCount
|
||||
}
|
||||
averages.normalSum > 0f && averages.normalCount > 0f -> {
|
||||
averageWithoutWeight && averages.normalSum > 0f && averages.normalCount > 0f -> {
|
||||
averages.normalSum / averages.normalCount
|
||||
}
|
||||
else -> null
|
||||
|
@ -1,22 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-2-16.
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-3-10.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.timetable
|
||||
package pl.szczodrzynski.edziennik.utils.managers
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.widget.TextView
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
|
||||
import pl.szczodrzynski.edziennik.resolveAttr
|
||||
import pl.szczodrzynski.edziennik.setText
|
||||
import pl.szczodrzynski.edziennik.setTintColor
|
||||
import pl.szczodrzynski.navlib.getColorFromAttr
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class TimetableManager(val app: App) : CoroutineScope {
|
||||
|
||||
private val job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Default
|
||||
|
||||
fun markAsSeen(lesson: LessonFull) {
|
||||
lesson.seen = true
|
||||
if (lesson.type <= Lesson.TYPE_NORMAL)
|
||||
return
|
||||
startCoroutineTimer(500L, 0L) {
|
||||
app.db.metadataDao().setSeen(lesson.profileId, lesson, true)
|
||||
}
|
||||
}
|
||||
|
||||
class TimetableUtils {
|
||||
fun getAnnotation(context: Context, lesson: LessonFull, annotation: TextView): Boolean {
|
||||
var annotationVisible = false
|
||||
when (lesson.type) {
|
@ -4,7 +4,8 @@
|
||||
-->
|
||||
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
@ -13,12 +14,21 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="24dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
style="@style/TextAppearance.AppCompat.Small"
|
||||
android:text="@string/grades_config_title"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
@ -28,7 +38,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:minHeight="0dp"
|
||||
android:text="Własna wartość plusa" />
|
||||
android:text="@string/grades_config_plus_value" />
|
||||
|
||||
<it.sephiroth.android.library.numberpicker.NumberPicker
|
||||
android:id="@+id/customPlusValue"
|
||||
@ -44,7 +54,7 @@
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
@ -54,7 +64,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:minHeight="0dp"
|
||||
android:text="Własna wartość minusa" />
|
||||
android:text="@string/grades_config_minus_value" />
|
||||
|
||||
<it.sephiroth.android.library.numberpicker.NumberPicker
|
||||
android:id="@+id/customMinusValue"
|
||||
@ -67,9 +77,55 @@
|
||||
app:picker_orientation="horizontal" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:background="@drawable/divider"/>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/dontCountZeroToAverage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="0dp"
|
||||
android:text="@string/settings_register_dont_count_zero_text"/>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/hideImproved"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="32dp"
|
||||
android:text="@string/grades_config_dont_show_improved"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/averageWithoutWeight"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:minHeight="32dp"
|
||||
android:text="@string/grades_config_average_without_weight"/>
|
||||
|
||||
<com.mikepenz.iconics.view.IconicsImageView
|
||||
android:id="@+id/averageWithoutWeightHelp"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:scaleType="centerInside"
|
||||
app:iiv_color="?android:textColorSecondary"
|
||||
app:iiv_icon="cmd-help-circle-outline"
|
||||
app:iiv_size="16dp"
|
||||
tools:src="@android:drawable/ic_menu_help" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
style="@style/TextAppearance.AppCompat.Small"
|
||||
android:text="@string/menu_grades_sort_mode"/>
|
||||
@ -167,21 +223,6 @@
|
||||
android:minHeight="0dp"
|
||||
android:text="@string/settings_register_avg_mode_3"/>
|
||||
</RadioGroup>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
style="@style/TextAppearance.AppCompat.Small"
|
||||
android:text="@string/other"/>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/dontCountZeroToAverage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="0dp"
|
||||
android:text="@string/settings_register_dont_count_zero_text"/>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</layout>
|
||||
|
@ -24,7 +24,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/dialog_event_manual_date">
|
||||
<pl.szczodrzynski.edziennik.utils.TextInputDropDown
|
||||
<pl.szczodrzynski.edziennik.ui.modules.views.DateDropdown
|
||||
android:id="@+id/dateDropdown"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -38,7 +38,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:hint="@string/dialog_event_manual_time">
|
||||
<pl.szczodrzynski.edziennik.utils.TextInputDropDown
|
||||
<pl.szczodrzynski.edziennik.ui.modules.views.TimeDropdown
|
||||
android:id="@+id/timeDropdown"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -53,7 +53,7 @@
|
||||
android:layout_marginTop="8dp"
|
||||
android:hint="@string/dialog_event_manual_team">
|
||||
|
||||
<pl.szczodrzynski.edziennik.utils.TextInputDropDown
|
||||
<pl.szczodrzynski.edziennik.ui.modules.views.TeamDropdown
|
||||
android:id="@+id/teamDropdown"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -158,7 +158,7 @@
|
||||
android:layout_marginTop="8dp"
|
||||
android:hint="@string/dialog_event_manual_subject">
|
||||
|
||||
<pl.szczodrzynski.edziennik.utils.TextInputDropDown
|
||||
<pl.szczodrzynski.edziennik.ui.modules.views.SubjectDropdown
|
||||
android:id="@+id/subjectDropdown"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -173,7 +173,7 @@
|
||||
android:layout_marginTop="8dp"
|
||||
android:hint="@string/dialog_event_manual_teacher">
|
||||
|
||||
<pl.szczodrzynski.edziennik.utils.TextInputDropDown
|
||||
<pl.szczodrzynski.edziennik.ui.modules.views.TeacherDropdown
|
||||
android:id="@+id/teacherDropdown"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -9,7 +9,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="20dp">
|
||||
android:paddingHorizontal="20dp"
|
||||
android:paddingTop="16dp">
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/weekSelectionRadioGroup"
|
||||
@ -36,10 +37,17 @@
|
||||
android:text="@string/timetable_generate_selected_week" />
|
||||
</RadioGroup>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@drawable/divider"/>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/showProfileNameCheckbox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="0dp"
|
||||
android:text="@string/timetable_generate_show_profile_name" />
|
||||
|
||||
<CheckBox
|
||||
@ -47,12 +55,14 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:minHeight="0dp"
|
||||
android:text="@string/timetable_generate_show_teachers_names" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/noColorsCheckbox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="0dp"
|
||||
android:text="@string/timetable_generate_no_colors" />
|
||||
</LinearLayout>
|
||||
</layout>
|
||||
|
@ -55,13 +55,12 @@
|
||||
android:layout_width="72dp"
|
||||
android:layout_height="72dp"
|
||||
android:background="@drawable/bg_rounded_16dp"
|
||||
android:fontFamily="serif-monospace"
|
||||
android:gravity="center"
|
||||
android:padding="8dp"
|
||||
android:text="@{grade.name}"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="36sp"
|
||||
android:textStyle="bold"
|
||||
app:autoSizeMinTextSize="18sp"
|
||||
app:autoSizeMaxTextSize="56sp"
|
||||
app:autoSizeTextType="uniform"
|
||||
tools:background="#ff4caf50"
|
||||
@ -270,10 +269,45 @@
|
||||
android:id="@+id/gradeHistoryList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:listitem="@layout/row_grades_list_item" />
|
||||
tools:listitem="@layout/grades_item_grade"
|
||||
tools:itemCount="2"/>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<View
|
||||
android:id="@+id/customValueDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/divider" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/customValueLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:text="@string/grades_stats_custom_value_notice"
|
||||
android:textAppearance="@style/NavView.TextView.Helper"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="italic" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/customValueButton"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="0dp"
|
||||
android:text="@string/configure" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</layout>
|
||||
|
@ -294,6 +294,12 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/divider"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/eventsNoData"
|
||||
android:layout_width="match_parent"
|
||||
@ -331,7 +337,6 @@
|
||||
android:id="@+id/eventsView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:clipToPadding="false"
|
||||
tools:visibility="visible"
|
||||
tools:listitem="@layout/event_list_item" />
|
||||
|
@ -20,6 +20,10 @@
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
tools:background="@drawable/bg_rounded_8dp"
|
||||
tools:backgroundTint="#4caf50"
|
||||
tools:textSize="24sp"
|
||||
tools:gravity="center"
|
||||
tools:text="5+" />
|
||||
|
||||
<LinearLayout
|
||||
@ -30,6 +34,7 @@
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
@ -38,12 +43,22 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
tools:text="kraje hehe no jak zwykle jedynka z geografii. to jest baaardzo długi tekst ale szkoda że się nie scrolluje." />
|
||||
tools:text="kraje" />
|
||||
|
||||
<View
|
||||
android:id="@+id/unread"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:visibility="gone"
|
||||
android:background="@drawable/unread_red_circle"
|
||||
tools:visibility="visible"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradeAddedDate"
|
||||
@ -81,7 +96,8 @@
|
||||
android:ellipsize="end"
|
||||
android:maxWidth="200dp"
|
||||
android:maxLines="1"
|
||||
tools:text="Kartkówki - K1 123456789 12345678" />
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
tools:text="Kartkówki - K1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradeTeacherName"
|
||||
@ -94,7 +110,7 @@
|
||||
android:gravity="end"
|
||||
android:maxLines="1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
tools:text="Anna Jakaśtam-Cośtam" />
|
||||
tools:text="Jan Kowalski" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
@ -37,6 +37,17 @@
|
||||
android:textSize="18sp"
|
||||
tools:text="Semestr 1" />
|
||||
|
||||
<View
|
||||
android:id="@+id/unread"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:visibility="gone"
|
||||
android:background="@drawable/unread_red_circle"
|
||||
tools:visibility="visible"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/average"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -20,7 +20,8 @@
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subjectName"
|
||||
@ -29,11 +30,22 @@
|
||||
android:layout_margin="8dp"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:fontFamily="sans-serif"
|
||||
android:maxLines="2"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="20sp"
|
||||
tools:text="systemy operacyjne\n1234" />
|
||||
tools:text="systemy operacyjne" />
|
||||
|
||||
<View
|
||||
android:id="@+id/unread"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:visibility="gone"
|
||||
android:background="@drawable/unread_red_circle"
|
||||
tools:visibility="visible"/>
|
||||
|
||||
<com.mikepenz.iconics.view.IconicsImageView
|
||||
android:id="@+id/dropdownIcon"
|
||||
@ -85,7 +97,7 @@
|
||||
tools:text1="Cały rok: 3 oceny • suma: 320 pkt"
|
||||
tools:text2="Cały rok: 15 ocen • średnia: 2,62"
|
||||
tools:text="Cały rok: 6 ocen • punkty: 34.20/40 (87.5%)"
|
||||
tools:visibility="gone"/>
|
||||
tools:visibility="visible"/>
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
</layout>
|
||||
|
@ -50,7 +50,8 @@
|
||||
android:background="@drawable/bg_rounded_8dp"
|
||||
android:gravity="center"
|
||||
android:textSize="24sp"
|
||||
app:autoSizeMaxTextSize="24sp"
|
||||
app:autoSizeMinTextSize="14sp"
|
||||
app:autoSizeMaxTextSize="32sp"
|
||||
app:autoSizeTextType="uniform"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
@ -1,104 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackground"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesListName"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:background="@drawable/bg_rounded_8dp"
|
||||
android:fontFamily="serif-monospace"
|
||||
android:gravity="center"
|
||||
android:padding="2dp"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
app:autoSizeMaxTextSize="32sp"
|
||||
app:autoSizeTextType="uniform"
|
||||
tools:background="#4caf50"
|
||||
tools:text="NB" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesListCategoryColumn"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
tools:text="kraje hehe no jak zwykle jedynka z geografii. to jest baaardzo długi tekst ale szkoda że się nie scrolluje." />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesListAddedDate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:textAppearance="@style/NavView.TextView.Helper"
|
||||
tools:text="14.10.2015" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesListWeight"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:textStyle="bold"
|
||||
tools:text="waga 30"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesListCategoryDescription"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:ellipsize="end"
|
||||
android:maxWidth="200dp"
|
||||
android:maxLines="1"
|
||||
tools:text="Kartkówki - K1 123456789 12345678" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesListTeacher"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:gravity="end"
|
||||
android:maxLines="1"
|
||||
android:textAppearance="@style/NavView.TextView.Helper"
|
||||
tools:text="Anna Jakaśtam-Cośtam" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
@ -1,459 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/gradesSubjectRoot"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesSubjectTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:text="@string/loading"
|
||||
android:textAppearance="@style/NavView.TextView.Title"
|
||||
app:layout_constraintEnd_toStartOf="@+id/gradesSubjectExpandIndicator"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="{SUBJECT_NAME}" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/gradesSubjectPreviewContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:orientation="horizontal"
|
||||
tools:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/gradesSubjectTitle">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesSubjectPreviewSemester"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:textStyle="italic"
|
||||
android:visibility="gone"
|
||||
tools:text="Semestr 1"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/gradesSubjectPreviewContent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal">
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesSubjectPreviewAverage"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
tools:text="4.70" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesSubjectPreviewProposed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:background="@drawable/bg_rounded_4dp_outline"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
tools:text="4"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesSubjectPreviewFinal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:background="@drawable/bg_rounded_4dp"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:textColor="@color/black"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
tools:text="5"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesSubjectPreviewYearAverage"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="5dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
tools:text="4.70"
|
||||
android:layout_marginEnd="5dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesSubjectPreviewYearProposed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:background="@drawable/bg_rounded_4dp_outline"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
tools:text="4"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesSubjectPreviewYearFinal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:background="@drawable/bg_rounded_4dp"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:textColor="@color/black"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
tools:text="5"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/gradesSubjectContent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/gradesSubjectTitle">
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/gradesSubjectSemester2Header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.mikepenz.iconics.view.IconicsImageView
|
||||
android:id="@+id/gradesSubjectSemester2ExpandIndicator"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:padding="8dp"
|
||||
app:iiv_color="?android:textColorSecondary"
|
||||
app:iiv_icon="cmd-menu-down"
|
||||
tools:srcCompat="@tools:sample/avatars" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesSubjectSemester2Title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="start"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/NavView.TextView.Medium"
|
||||
android:text="@string/grades_semester2_header" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="end"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesSubjectYearAverage"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:layout_weight="1"
|
||||
tools:text="koniec roku: 4.70" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesSubjectYearProposed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:background="@drawable/bg_rounded_4dp_outline"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textStyle="bold"
|
||||
tools:text="4" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesSubjectYearFinal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:background="@drawable/bg_rounded_4dp"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:textColor="@color/black"
|
||||
android:textStyle="bold"
|
||||
tools:text="5" />
|
||||
</LinearLayout>
|
||||
|
||||
<Space
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="5dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesSubjectSemester2Average"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:layout_weight="1"
|
||||
tools:text="semestr 2: 5.00" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesSubjectSemester2Proposed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:background="@drawable/bg_rounded_4dp_outline"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textStyle="bold"
|
||||
tools:text="5" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesSubjectSemester2Final"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:background="@drawable/bg_rounded_4dp"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:textColor="@color/black"
|
||||
android:textStyle="bold"
|
||||
tools:text="5" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.mikepenz.iconics.view.IconicsImageView
|
||||
android:id="@+id/gradesSubjectSemester2EditButton"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="?selectableItemBackground"
|
||||
android:padding="8dp"
|
||||
app:iiv_color="?android:textColorSecondary"
|
||||
app:iiv_icon="cmd-pencil"
|
||||
tools:srcCompat="@tools:sample/avatars" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/gradesSubjectSemester2Container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="visible">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/gradesSubjectSemester2Nest"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/gradesSubjectSemester2Content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:itemCount="3"
|
||||
tools:listitem="@layout/row_grades_list_item" />
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/gradesSubjectSemester1Header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.mikepenz.iconics.view.IconicsImageView
|
||||
android:id="@+id/gradesSubjectSemester1ExpandIndicator"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:padding="8dp"
|
||||
app:iiv_color="?android:textColorSecondary"
|
||||
app:iiv_icon="cmd-menu-down"
|
||||
tools:srcCompat="@tools:sample/avatars" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesSubjectSemester1Title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="start"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/NavView.TextView.Medium"
|
||||
android:text="@string/grades_semester1_header"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="end"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesSubjectSemester1Average"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:layout_weight="1"
|
||||
tools:text="semestr 1: 0.63" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesSubjectSemester1Proposed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:background="@drawable/bg_rounded_4dp_outline"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textStyle="bold"
|
||||
tools:text="1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradesSubjectSemester1Final"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:background="@drawable/bg_rounded_4dp"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:textColor="@color/black"
|
||||
android:textStyle="bold"
|
||||
tools:text="2" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.mikepenz.iconics.view.IconicsImageView
|
||||
android:id="@+id/gradesSubjectSemester1EditButton"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="?selectableItemBackground"
|
||||
android:padding="8dp"
|
||||
app:iiv_color="?android:textColorSecondary"
|
||||
app:iiv_icon="cmd-pencil"
|
||||
tools:srcCompat="@tools:sample/avatars" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/gradesSubjectSemester1Container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/gradesSubjectSemester1Nest"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/gradesSubjectSemester1Content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:itemCount="5"
|
||||
tools:listitem="@layout/row_grades_list_item" />
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.mikepenz.iconics.view.IconicsImageView
|
||||
android:id="@+id/gradesSubjectExpandIndicator"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:padding="8dp"
|
||||
app:iiv_color="?android:textColorSecondary"
|
||||
app:iiv_icon="cmd-menu-down"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:srcCompat="@tools:sample/avatars[0]" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -74,8 +74,8 @@
|
||||
tools:text="pracownia urządzeń techniki komputerowej" />
|
||||
|
||||
<View
|
||||
android:layout_width="8dp"
|
||||
android:layout_height="8dp"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
|
@ -188,7 +188,7 @@
|
||||
<string name="error_56_reason">Błąd serwera: odpowiedź niedostępna</string>
|
||||
<string name="error_57_reason">Błąd serwera: niespełnione zależności</string>
|
||||
<string name="error_58_reason">Wewnętrzny błąd serwera</string>
|
||||
<string name="error_59_reason">Usługa tymczasowo niedostępna</string>
|
||||
<string name="error_59_reason">Dziennik jest tymczasowo niedostępny</string>
|
||||
<string name="error_60_reason">Brak internetu: nie znaleziono adresu serwera</string>
|
||||
<string name="error_61_reason">Brak internetu: przekroczono czas oczekiwania</string>
|
||||
<string name="error_62_reason">Brak internetu</string>
|
||||
|
@ -1043,7 +1043,7 @@
|
||||
<string name="dialog_event_manual_date_this_week" translatable="false">%s (%s)</string>
|
||||
<string name="dialog_event_manual_date_other">-- inna data --</string>
|
||||
<string name="dialog_event_manual_date_today">dzisiaj (%s)</string>
|
||||
<string name="dialog_event_manual_date_next_week">następny %s (%s)</string>
|
||||
<string name="dialog_event_manual_date_next_week">przyszły %s (%s)</string>
|
||||
<string name="dialog_event_manual_no_lessons">Nie ma lekcji tego dnia</string>
|
||||
<string name="dialog_profile_remove_success">Profil został usunięty.</string>
|
||||
<string name="snackbar_error_text">Wystąpił błąd</string>
|
||||
@ -1233,4 +1233,28 @@
|
||||
<string name="grades_stats_help_text">Ocena przewidywana z danego przedmiotu jest obliczana na podstawie aktualnej średniej ważonej.\n\nOcena jest liczbą całkowitą, jaką wystawił by nauczyciel bazując na średniej. Liczba zaokrąglona jest w górę jeśli część po przecinku przekroczy ,75.\nPrzykładowo: średnie 3,75 jak również 4,74 dają w wyniku ocenę dobrą (4).\n\nŚrednia przewidywana ze wszystkich przedmiotów obejmuje obliczone w ten sposób oceny końcowe.</string>
|
||||
<string name="grades_stats_custom_value_notice">Została ustawiona własna wartość plusa/minusa. Jeśli uważasz, że średnia się nie zgadza, kliknij Konfiguruj.</string>
|
||||
<string name="configure">Konfiguruj</string>
|
||||
<string name="menu_timetable_manual">Edytor planu lekcji</string>
|
||||
<string name="dropdown_subject_custom">Własny przedmiot</string>
|
||||
<string name="dialog_event_manual_date_choose">Wybierz datę</string>
|
||||
<string name="dropdown_date_no_more_lessons">Nie ma więcej lekcji tego przedmiotu. Pobierz plan lekcji i spróbuj ponownie.</string>
|
||||
<string name="timetable_manual_dialog_date">Data</string>
|
||||
<string name="timetable_manual_dialog_date_choose">Wybierz datę</string>
|
||||
<string name="timetable_manual_dialog_subject_choose">Wybierz przedmiot</string>
|
||||
<string name="timetable_manual_dialog_time">Godzina</string>
|
||||
<string name="timetable_manual_dialog_time_choose">Wybierz godzinę</string>
|
||||
<string name="timetable_manual_dialog_time_format" formatted="false">%s (lekcja %d)</string>
|
||||
<string name="timetable_manual_item_empty">Nie dodano własnych lekcji na ten dzień.</string>
|
||||
<string name="timetable_manual_repeat_by_subject_notice">Na każdej lekcji przedmiotu %s</string>
|
||||
<string name="timetable_manual_repeating_notice">Obejmuje plan lekcji na każdy tydzień</string>
|
||||
<string name="timetable_manual_type_by_subject">Wg. przedmiotu</string>
|
||||
<string name="timetable_manual_type_one_time">Jednorazowo</string>
|
||||
<string name="timetable_manual_type_repeating">Cyklicznie</string>
|
||||
<string name="grades_config_title">Konfiguracja ocen</string>
|
||||
<string name="grades_config_dont_show_improved">Ukrywaj oceny poprawione z listy</string>
|
||||
<string name="grades_config_average_without_weight">Licz średnią jeśli wszystkie wagi to 0</string>
|
||||
<string name="grades_config_average_without_weight_message">Pozwala na liczenie średniej arytmetycznej z przedmiotów, w których wszystkie wystawione oceny mają wagę 0 (nie są liczone do średniej).\n\nJeśli taki przedmiot celowo nie powinien być liczony, odznacz okienko przy tym ustawieniu.</string>
|
||||
<string name="grades_config_minus_value">Własna wartość minusa</string>
|
||||
<string name="grades_config_plus_value">Własna wartość plusa</string>
|
||||
<string name="timetable_syncing_text">Pobieranie planu lekcji na wybrany tydzień...</string>
|
||||
<string name="dialog_event_manual_no_timetable">Nie pobrano planu lekcji...</string>
|
||||
</resources>
|
||||
|
@ -5,8 +5,8 @@ buildscript {
|
||||
kotlin_version = '1.3.61'
|
||||
|
||||
release = [
|
||||
versionName: "4.0-beta.11",
|
||||
versionCode: 4000011
|
||||
versionName: "4.0-beta.12",
|
||||
versionCode: 4000012
|
||||
]
|
||||
|
||||
setup = [
|
||||
|
Reference in New Issue
Block a user