mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-06-13 14:10:46 +02:00
Compare commits
46 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 | |||
55268f1c43 | |||
1bec6d281c | |||
f17a02be54 | |||
4e8fdd2225 | |||
59819b4a96 | |||
673378d8d9 | |||
30044d6b21 | |||
ee43d40680 | |||
1354faf8c7 | |||
1bfb3781ab | |||
d7d0c6f822 | |||
2bea18dc3c | |||
f998f2d956 | |||
faa77ee5fb | |||
88ec463284 | |||
b7df71d7d9 | |||
6a28dbd2c4 | |||
010f7fa1fe | |||
209f98594f | |||
54121c99a3 | |||
f6f1370edf | |||
d5863485f9 |
@ -166,8 +166,8 @@ dependencies {
|
||||
//implementation project(":Navigation")
|
||||
implementation project(":szkolny-font")
|
||||
|
||||
debugImplementation "com.github.ChuckerTeam.Chucker:library:3.0.1"
|
||||
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:3.0.1"
|
||||
implementation "com.github.ChuckerTeam.Chucker:library:3.0.1"
|
||||
//releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:3.0.1"
|
||||
|
||||
//implementation 'com.github.wulkanowy:uonet-request-signer:master-SNAPSHOT'
|
||||
//implementation 'com.github.kuba2k2.uonet-request-signer:android:master-63f094b14a-1'
|
||||
@ -190,6 +190,8 @@ dependencies {
|
||||
implementation 'com.github.jetradarmobile:android-snowfall:1.2.0'
|
||||
|
||||
implementation "io.coil-kt:coil:0.9.2"
|
||||
|
||||
implementation 'com.github.kuba2k2:NumberSlidingPicker:2921225f76'
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
@ -1,19 +1,23 @@
|
||||
<h3>Wersja 4.0-beta.10, 2020-02-24</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] = {
|
||||
0xd6, 0x0d, 0xa0, 0xa7, 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);
|
||||
|
||||
|
@ -45,7 +45,9 @@ import pl.szczodrzynski.edziennik.sync.SyncWorker
|
||||
import pl.szczodrzynski.edziennik.sync.UpdateWorker
|
||||
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
|
||||
@ -60,10 +62,13 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
|
||||
get() = profile.id
|
||||
|
||||
var devMode = false
|
||||
var debugMode = false
|
||||
}
|
||||
|
||||
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
|
||||
@ -103,7 +108,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
|
||||
.readTimeout(10, TimeUnit.SECONDS)
|
||||
builder.installHttpsSupport(this)
|
||||
|
||||
if (devMode || BuildConfig.DEBUG) {
|
||||
if (debugMode || BuildConfig.DEBUG) {
|
||||
HyperLog.initialize(this)
|
||||
HyperLog.setLogLevel(Log.VERBOSE)
|
||||
HyperLog.setLogFormat(DebugLogFormat(this))
|
||||
@ -158,6 +163,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
|
||||
Iconics.registerFont(SzkolnyFont)
|
||||
App.db = AppDb(this)
|
||||
Themes.themeInt = config.ui.theme
|
||||
debugMode = config.debugMode
|
||||
MHttp.instance().customOkHttpClient(http)
|
||||
|
||||
if (!profileLoadById(config.lastProfileId)) {
|
||||
@ -174,6 +180,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
|
||||
|
||||
if (config.devModePassword != null)
|
||||
checkDevModePassword()
|
||||
debugMode = devMode || config.debugMode
|
||||
|
||||
if (config.sync.enabled)
|
||||
SyncWorker.scheduleNext(this@App, false)
|
||||
|
@ -1080,3 +1080,15 @@ fun Throwable.toErrorCode() = when (this) {
|
||||
private fun ApiResponse.Error.toErrorCode() = when (this.code) {
|
||||
else -> ERROR_API_EXCEPTION
|
||||
}
|
||||
|
||||
inline fun <A, B, R> ifNotNull(a: A?, b: B?, code: (A, B) -> R): R? {
|
||||
if (a != null && b != null) {
|
||||
return code(a, b)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@kotlin.jvm.JvmName("averageOrNullOfInt")
|
||||
fun Iterable<Int>.averageOrNull() = this.average().let { if (it.isNaN()) null else it }
|
||||
@kotlin.jvm.JvmName("averageOrNullOfFloat")
|
||||
fun Iterable<Float>.averageOrNull() = this.average().let { if (it.isNaN()) null else it }
|
||||
|
@ -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()
|
||||
@ -75,6 +75,11 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
|
||||
get() { mPrivacyPolicyAccepted = mPrivacyPolicyAccepted ?: values.get("privacyPolicyAccepted", false); return mPrivacyPolicyAccepted ?: false }
|
||||
set(value) { set("privacyPolicyAccepted", value); mPrivacyPolicyAccepted = value }
|
||||
|
||||
private var mDebugMode: Boolean? = null
|
||||
var debugMode: Boolean
|
||||
get() { mDebugMode = mDebugMode ?: values.get("debugMode", false); return mDebugMode ?: false }
|
||||
set(value) { set("debugMode", value); mDebugMode = value }
|
||||
|
||||
private var mDevModePassword: String? = null
|
||||
var devModePassword: String?
|
||||
get() { mDevModePassword = mDevModePassword ?: values.get("devModePassword", null as String?); return mDevModePassword }
|
||||
|
@ -6,17 +6,11 @@ package pl.szczodrzynski.edziennik.config
|
||||
|
||||
import pl.szczodrzynski.edziennik.config.utils.get
|
||||
import pl.szczodrzynski.edziennik.config.utils.set
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager
|
||||
|
||||
class ConfigGrades(private val config: Config) {
|
||||
companion object {
|
||||
const val ORDER_BY_DATE_DESC = 0
|
||||
const val ORDER_BY_SUBJECT_ASC = 1
|
||||
const val ORDER_BY_DATE_ASC = 2
|
||||
const val ORDER_BY_SUBJECT_DESC = 3
|
||||
}
|
||||
|
||||
private var mOrderBy: Int? = null
|
||||
var orderBy: Int
|
||||
get() { mOrderBy = mOrderBy ?: config.values.get("gradesOrderBy", 0); return mOrderBy ?: ORDER_BY_DATE_DESC }
|
||||
get() { mOrderBy = mOrderBy ?: config.values.get("gradesOrderBy", 0); return mOrderBy ?: GradesManager.ORDER_BY_DATE_DESC }
|
||||
set(value) { config.set("gradesOrderBy", value); mOrderBy = value }
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
@ -90,4 +96,4 @@ class ConfigSync(private val config: Config) {
|
||||
var tokenVulcanList: List<Int>
|
||||
get() { mTokenVulcanList = mTokenVulcanList ?: config.values.getIntList("tokenVulcanList", listOf()); return mTokenVulcanList ?: listOf() }
|
||||
set(value) { config.set("tokenVulcanList", value); mTokenVulcanList = value }
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ 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.ui.modules.home.HomeCardModel
|
||||
|
||||
class ConfigUI(private val config: Config) {
|
||||
private var mTheme: Int? = null
|
||||
@ -45,11 +44,6 @@ class ConfigUI(private val config: Config) {
|
||||
get() { mOpenDrawerOnBackPressed = mOpenDrawerOnBackPressed ?: config.values.get("openDrawerOnBackPressed", false); return mOpenDrawerOnBackPressed ?: false }
|
||||
set(value) { config.set("openDrawerOnBackPressed", value); mOpenDrawerOnBackPressed = value }
|
||||
|
||||
private var mHomeCards: List<HomeCardModel>? = null
|
||||
var homeCards: List<HomeCardModel>
|
||||
get() { mHomeCards = mHomeCards ?: config.values.get("homeCards", listOf(), HomeCardModel::class.java); return mHomeCards ?: listOf() }
|
||||
set(value) { config.set("homeCards", value); mHomeCards = value }
|
||||
|
||||
private var mSnowfall: Boolean? = null
|
||||
var snowfall: Boolean
|
||||
get() { mSnowfall = mSnowfall ?: config.values.get("snowfall", false); return mSnowfall ?: false }
|
||||
|
@ -5,9 +5,10 @@
|
||||
package pl.szczodrzynski.edziennik.config
|
||||
|
||||
import pl.szczodrzynski.edziennik.config.utils.get
|
||||
import pl.szczodrzynski.edziennik.config.utils.getFloat
|
||||
import pl.szczodrzynski.edziennik.config.utils.set
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.COLOR_MODE_WEIGHTED
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.YEAR_ALL_GRADES
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_WEIGHTED
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES
|
||||
|
||||
class ProfileConfigGrades(private val config: ProfileConfig) {
|
||||
private var mColorMode: Int? = null
|
||||
@ -24,4 +25,28 @@ class ProfileConfigGrades(private val config: ProfileConfig) {
|
||||
var countZeroToAvg: Boolean
|
||||
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 }
|
||||
set(value) { config.set("plusValue", value); mPlusValue = value }
|
||||
private var mMinusValue: Float? = null
|
||||
var minusValue: Float?
|
||||
get() { mMinusValue = mMinusValue ?: config.values.getFloat("minusValue"); return mMinusValue }
|
||||
set(value) { config.set("minusValue", value); mMinusValue = value }
|
||||
|
||||
private var mDontCountGrades: List<String>? = null
|
||||
var dontCountGrades: List<String>
|
||||
get() { mDontCountGrades = mDontCountGrades ?: config.values.get("dontCountGrades", listOf()); return mDontCountGrades ?: listOf() }
|
||||
set(value) { config.set("dontCountGrades", value); mDontCountGrades = value }
|
||||
}
|
||||
|
@ -7,10 +7,16 @@ package pl.szczodrzynski.edziennik.config
|
||||
import pl.szczodrzynski.edziennik.config.utils.get
|
||||
import pl.szczodrzynski.edziennik.config.utils.set
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardModel
|
||||
|
||||
class ProfileConfigUI(private val config: ProfileConfig) {
|
||||
private var mAgendaViewType: Int? = null
|
||||
var agendaViewType: Int
|
||||
get() { mAgendaViewType = mAgendaViewType ?: config.values.get("agendaViewType", 0); return mAgendaViewType ?: AGENDA_DEFAULT }
|
||||
set(value) { config.set("agendaViewType", value); mAgendaViewType = value }
|
||||
|
||||
private var mHomeCards: List<HomeCardModel>? = null
|
||||
var homeCards: List<HomeCardModel>
|
||||
get() { mHomeCards = mHomeCards ?: config.values.get("homeCards", listOf(), HomeCardModel::class.java); return mHomeCards ?: listOf() }
|
||||
set(value) { config.set("homeCards", value); mHomeCards = value }
|
||||
}
|
||||
|
@ -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()
|
||||
@ -78,4 +93,4 @@ class AppConfigMigrationV3(p: SharedPreferences, config: Config) {
|
||||
private fun String?.fix(): String? {
|
||||
return this?.replace("\"", "")?.let { if (it == "null") null else it }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,10 +94,14 @@ fun HashMap<String, String?>.getLongList(key: String, default: List<Long>?): Lis
|
||||
return this[key]?.let { gson.fromJson<List<Long>>(it, object: TypeToken<List<Long>>(){}.type) } ?: default
|
||||
}
|
||||
|
||||
fun HashMap<String, String?>.getFloat(key: String): Float? {
|
||||
return this[key]?.toFloatOrNull()
|
||||
}
|
||||
|
||||
fun List<ConfigEntry>.toHashMap(profileId: Int, map: HashMap<String, String?>) {
|
||||
map.clear()
|
||||
forEach {
|
||||
if (it.profileId == profileId)
|
||||
map[it.key] = it.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,9 @@ import pl.szczodrzynski.edziennik.BuildConfig
|
||||
import pl.szczodrzynski.edziennik.HOUR
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.config.Config
|
||||
import pl.szczodrzynski.edziennik.config.ConfigGrades.Companion.ORDER_BY_DATE_DESC
|
||||
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
|
||||
@ -63,12 +66,31 @@ class ConfigMigration(app: App, config: Config) {
|
||||
|
||||
if (dataVersion < 10) {
|
||||
ui.openDrawerOnBackPressed = false
|
||||
ui.homeCards = listOf()
|
||||
ui.snowfall = false
|
||||
ui.bottomSheetOpened = false
|
||||
sync.dontShowAppManagerDialog = false
|
||||
|
||||
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
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ package pl.szczodrzynski.edziennik.config.utils
|
||||
|
||||
import pl.szczodrzynski.edziennik.config.ProfileConfig
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.COLOR_MODE_WEIGHTED
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.YEAR_ALL_GRADES
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_WEIGHTED
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES
|
||||
|
||||
class ProfileConfigMigration(config: ProfileConfig) {
|
||||
init { config.apply {
|
||||
@ -21,4 +21,4 @@ class ProfileConfigMigration(config: ProfileConfig) {
|
||||
dataVersion = 1
|
||||
}
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ const val IDZIENNIK_USER_AGENT = SYNERGIA_USER_AGENT
|
||||
const val IDZIENNIK_WEB_URL = "https://iuczniowie.progman.pl/idziennik"
|
||||
const val IDZIENNIK_WEB_LOGIN = "login.aspx"
|
||||
const val IDZIENNIK_WEB_SETTINGS = "mod_panelRodzica/Ustawienia.aspx"
|
||||
const val IDZIENNIK_WEB_HOME = "mod_panelRodzica/StronaGlowna.aspx"
|
||||
const val IDZIENNIK_WEB_TIMETABLE = "mod_panelRodzica/plan/WS_Plan.asmx/pobierzPlanZajec"
|
||||
const val IDZIENNIK_WEB_GRADES = "mod_panelRodzica/oceny/WS_ocenyUcznia.asmx/pobierzOcenyUcznia"
|
||||
const val IDZIENNIK_WEB_MISSING_GRADES = "mod_panelRodzica/brak_ocen/WS_BrakOcenUcznia.asmx/pobierzBrakujaceOcenyUcznia"
|
||||
|
@ -123,6 +123,7 @@ const val ERROR_LIBRUS_API_MAINTENANCE = 181
|
||||
const val ERROR_LIBRUS_PORTAL_MAINTENANCE = 182
|
||||
const val ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM = 183
|
||||
const val ERROR_LOGIN_LIBRUS_PORTAL_CSRF_EXPIRED = 184
|
||||
const val ERROR_LIBRUS_API_DEVICE_REGISTERED = 185
|
||||
|
||||
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN = 201
|
||||
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD = 202
|
||||
|
@ -109,6 +109,13 @@ object Regexes {
|
||||
val IDZIENNIK_MESSAGES_RECIPIENT_PARENT by lazy {
|
||||
"""(.+?)\s\((.+)\)""".toRegex()
|
||||
}
|
||||
/*<span id="ctl00_spanSzczesliwyLos">Szczęśliwy los na dzisiaj to <b>19</b>. Los na jutro to <b>22</b></span>*/
|
||||
val IDZIENNIK_WEB_LUCKY_NUMBER by lazy {
|
||||
"""dzisiaj to <b>([0-9]+)</b>""".toRegex()
|
||||
}
|
||||
val IDZIENNIK_WEB_SELECTED_REGISTER by lazy {
|
||||
"""selected="selected" value="([0-9]+)" data-id-ucznia""".toRegex()
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -27,8 +27,15 @@ class EdudziennikWebAttendance(override val data: DataEdudziennik,
|
||||
private const val TAG = "EdudziennikWebAttendance"
|
||||
}
|
||||
|
||||
init { data.profile?.also { profile ->
|
||||
webGet(TAG, data.studentEndpoint + "Presence", semester = -1) { text ->
|
||||
private var requestSemester: Int? = null
|
||||
|
||||
init {
|
||||
if (profile?.empty == true && data.currentSemester == 2) requestSemester = 1
|
||||
getAttendances()
|
||||
}
|
||||
|
||||
private fun getAttendances() { data.profile?.also { profile ->
|
||||
webGet(TAG, data.studentEndpoint + "Presence", semester = requestSemester) { text ->
|
||||
|
||||
val attendanceTypes = EDUDZIENNIK_ATTENDANCE_TYPES.find(text)?.get(1)?.split(',')?.map {
|
||||
val type = EDUDZIENNIK_ATTENDANCE_TYPE.find(it.trim())
|
||||
@ -91,8 +98,13 @@ class EdudziennikWebAttendance(override val data: DataEdudziennik,
|
||||
}
|
||||
}
|
||||
|
||||
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE, SYNC_ALWAYS)
|
||||
onSuccess(ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE)
|
||||
if (profile.empty && requestSemester == 1 && data.currentSemester == 2) {
|
||||
requestSemester = null
|
||||
getAttendances()
|
||||
} else {
|
||||
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE, SYNC_ALWAYS)
|
||||
onSuccess(ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE)
|
||||
}
|
||||
}
|
||||
} ?: onSuccess(ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE) }
|
||||
}
|
||||
|
@ -14,7 +14,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZI
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
|
||||
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||
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_SUM
|
||||
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.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.get
|
||||
@ -90,7 +95,7 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
|
||||
val columnName = info.child(4).text().trim()
|
||||
val comment = info.ownText()
|
||||
|
||||
val description = columnName + if (comment.isNotBlank()) " - $comment" else ""
|
||||
val description = columnName + if (comment.isNotBlank()) " - $comment" else null
|
||||
|
||||
val teacherName = info.child(1).text()
|
||||
val teacher = data.getTeacherByLastFirst(teacherName)
|
||||
@ -109,20 +114,20 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
|
||||
} ?: -1
|
||||
|
||||
val gradeObject = Grade(
|
||||
profileId,
|
||||
id,
|
||||
fullName,
|
||||
color,
|
||||
description,
|
||||
name,
|
||||
value,
|
||||
if (gradeCountToAverage) weight else 0f,
|
||||
semester,
|
||||
teacher.id,
|
||||
subject.id
|
||||
).apply {
|
||||
type = gradeType
|
||||
}
|
||||
profileId = profileId,
|
||||
id = id,
|
||||
name = name,
|
||||
type = gradeType,
|
||||
value = value,
|
||||
weight = if (gradeCountToAverage) weight else 0f,
|
||||
color = color,
|
||||
category = fullName,
|
||||
description = description,
|
||||
comment = null,
|
||||
semester = semester,
|
||||
teacherId = teacher.id,
|
||||
subjectId = subject.id
|
||||
)
|
||||
|
||||
data.gradeList.add(gradeObject)
|
||||
data.metadataList.add(Metadata(
|
||||
@ -139,23 +144,23 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
|
||||
|
||||
if (proposed != null && proposed.isNotBlank()) {
|
||||
val proposedGradeObject = Grade(
|
||||
profileId,
|
||||
(-1 * subject.id) - 1,
|
||||
"",
|
||||
-1,
|
||||
"",
|
||||
proposed,
|
||||
proposed.toFloatOrNull() ?: 0f,
|
||||
0f,
|
||||
semester,
|
||||
-1,
|
||||
subject.id
|
||||
).apply {
|
||||
type = when (semester) {
|
||||
1 -> TYPE_SEMESTER1_PROPOSED
|
||||
else -> TYPE_SEMESTER2_PROPOSED
|
||||
}
|
||||
}
|
||||
profileId = profileId,
|
||||
id = (-1 * subject.id) - 1,
|
||||
name = proposed,
|
||||
type = when (semester) {
|
||||
1 -> TYPE_SEMESTER1_PROPOSED
|
||||
else -> TYPE_SEMESTER2_PROPOSED
|
||||
},
|
||||
value = proposed.toFloatOrNull() ?: 0f,
|
||||
weight = 0f,
|
||||
color = -1,
|
||||
category = null,
|
||||
description = null,
|
||||
comment = null,
|
||||
semester = semester,
|
||||
teacherId = -1,
|
||||
subjectId = subject.id
|
||||
)
|
||||
|
||||
data.gradeList.add(proposedGradeObject)
|
||||
data.metadataList.add(Metadata(
|
||||
@ -172,23 +177,23 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
|
||||
|
||||
if (final != null && final.isNotBlank()) {
|
||||
val finalGradeObject = Grade(
|
||||
profileId,
|
||||
(-1 * subject.id) - 2,
|
||||
"",
|
||||
-1,
|
||||
"",
|
||||
final,
|
||||
final.toFloatOrNull() ?: 0f,
|
||||
0f,
|
||||
semester,
|
||||
-1,
|
||||
subject.id
|
||||
).apply {
|
||||
type = when (semester) {
|
||||
1 -> TYPE_SEMESTER1_FINAL
|
||||
else -> TYPE_SEMESTER2_FINAL
|
||||
}
|
||||
}
|
||||
profileId = profileId,
|
||||
id = (-1 * subject.id) - 2,
|
||||
name = final,
|
||||
type = when (semester) {
|
||||
1 -> TYPE_SEMESTER1_FINAL
|
||||
else -> TYPE_SEMESTER2_FINAL
|
||||
},
|
||||
value = final.toFloatOrNull() ?: 0f,
|
||||
weight = 0f,
|
||||
color = -1,
|
||||
category = null,
|
||||
description = null,
|
||||
comment = null,
|
||||
semester = semester,
|
||||
teacherId = -1,
|
||||
subjectId = subject.id
|
||||
)
|
||||
|
||||
data.gradeList.add(finalGradeObject)
|
||||
data.metadataList.add(Metadata(
|
||||
|
@ -81,6 +81,11 @@ class DataIdziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data(
|
||||
get() { mWebAuth = mWebAuth ?: loginStore.getLoginData("webAuth", null); return mWebAuth }
|
||||
set(value) { loginStore.putLoginData("webAuth", value); mWebAuth = value }
|
||||
|
||||
private var mWebSelectedRegister: Int? = null
|
||||
var webSelectedRegister: Int
|
||||
get() { mWebSelectedRegister = mWebSelectedRegister ?: loginStore.getLoginData("webSelectedRegister", 0); return mWebSelectedRegister ?: 0 }
|
||||
set(value) { loginStore.putLoginData("webSelectedRegister", value); mWebSelectedRegister = value }
|
||||
|
||||
/* _
|
||||
/\ (_)
|
||||
/ \ _ __ _
|
||||
|
@ -13,6 +13,7 @@ import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebSwitchRegister
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import java.io.File
|
||||
@ -48,6 +49,17 @@ open class IdziennikWeb(open val data: DataIdziennik, open val lastSync: Long?)
|
||||
return
|
||||
}
|
||||
|
||||
if (response?.code() == HTTP_INTERNAL_ERROR && endpoint == IDZIENNIK_WEB_GRADES) {
|
||||
// special override for accounts where displaying grades
|
||||
// for another student requires switching it manually
|
||||
if (data.registerId != data.webSelectedRegister) {
|
||||
IdziennikWebSwitchRegister(data, data.registerId) {
|
||||
webApiGet(tag, endpoint, parameters, onSuccess)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
when {
|
||||
response?.code() == HTTP_UNAUTHORIZED -> ERROR_IDZIENNIK_WEB_ACCESS_DENIED
|
||||
response?.code() == HTTP_INTERNAL_ERROR -> ERROR_IDZIENNIK_WEB_SERVER_ERROR
|
||||
@ -115,7 +127,7 @@ open class IdziennikWeb(open val data: DataIdziennik, open val lastSync: Long?)
|
||||
.enqueue()
|
||||
}
|
||||
|
||||
fun webGet(tag: String, endpoint: String, onSuccess: (text: String) -> Unit) {
|
||||
fun webGet(tag: String, endpoint: String, parameters: Map<String, Any> = emptyMap(), onSuccess: (text: String) -> Unit) {
|
||||
d(tag, "Request: Idziennik/Web - $IDZIENNIK_WEB_URL/$endpoint")
|
||||
|
||||
val callback = object : TextCallbackHandler() {
|
||||
@ -160,7 +172,14 @@ open class IdziennikWeb(open val data: DataIdziennik, open val lastSync: Long?)
|
||||
Request.builder()
|
||||
.url("$IDZIENNIK_WEB_URL/$endpoint")
|
||||
.userAgent(IDZIENNIK_USER_AGENT)
|
||||
.get()
|
||||
.apply {
|
||||
if (parameters.isEmpty()) get()
|
||||
else post()
|
||||
|
||||
parameters.map { (name, value) ->
|
||||
addParameter(name, value)
|
||||
}
|
||||
}
|
||||
.callback(callback)
|
||||
.build()
|
||||
.enqueue()
|
||||
|
@ -69,7 +69,7 @@ class IdziennikApiCurrentRegister(override val data: DataIdziennik,
|
||||
|
||||
val luckyNumberObject = LuckyNumber(
|
||||
data.profileId,
|
||||
Date.getToday(),
|
||||
luckyNumberDate,
|
||||
luckyNumber
|
||||
)
|
||||
|
||||
|
@ -17,6 +17,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import java.util.*
|
||||
|
||||
class IdziennikWebExams(override val data: DataIdziennik,
|
||||
override val lastSync: Long?,
|
||||
@ -70,9 +71,12 @@ class IdziennikWebExams(override val data: DataIdziennik,
|
||||
val lessonList = data.db.timetableDao().getForDateNow(profileId, examDate)
|
||||
val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.startTime
|
||||
|
||||
val eventType = when (exam.getString("rodzaj")) {
|
||||
"sprawdzian/praca klasowa" -> Event.TYPE_EXAM
|
||||
else -> Event.TYPE_SHORT_QUIZ
|
||||
val eventType = when (exam.getString("rodzaj")?.toLowerCase(Locale.getDefault())) {
|
||||
"sprawdzian/praca klasowa",
|
||||
"sprawdzian",
|
||||
"praca klasowa" -> Event.TYPE_EXAM
|
||||
"kartkówka" -> Event.TYPE_SHORT_QUIZ
|
||||
else -> Event.TYPE_EXAM
|
||||
}
|
||||
|
||||
val eventObject = Event(
|
||||
|
@ -14,6 +14,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
|
||||
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.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
@ -63,17 +64,19 @@ class IdziennikWebGrades(override val data: DataIdziennik,
|
||||
}
|
||||
|
||||
val gradeObject = Grade(
|
||||
profileId,
|
||||
id,
|
||||
category,
|
||||
colorInt,
|
||||
"",
|
||||
name,
|
||||
value,
|
||||
weight,
|
||||
semester,
|
||||
teacher.id,
|
||||
subject.id)
|
||||
profileId = profileId,
|
||||
id = id,
|
||||
name = name,
|
||||
type = TYPE_NORMAL,
|
||||
value = value,
|
||||
weight = weight,
|
||||
color = colorInt,
|
||||
category = category,
|
||||
description = null,
|
||||
comment = null,
|
||||
semester = semester,
|
||||
teacherId = teacher.id,
|
||||
subjectId = subject.id)
|
||||
|
||||
when (grade.getInt("Typ")) {
|
||||
0 -> {
|
||||
@ -98,17 +101,19 @@ class IdziennikWebGrades(override val data: DataIdziennik,
|
||||
}
|
||||
|
||||
val historyObject = Grade(
|
||||
profileId,
|
||||
gradeObject.id * -1,
|
||||
historyItem.get("Kategoria").asString,
|
||||
colorInt,
|
||||
historyItem.get("Uzasadnienie").asString,
|
||||
historyItem.get("Ocena").asString,
|
||||
value,
|
||||
if (value > 0f && countToTheAverage) weight * -1f else 0f,
|
||||
historyItem.get("Semestr").asInt,
|
||||
teacher.id,
|
||||
subject.id)
|
||||
profileId = profileId,
|
||||
id = gradeObject.id * -1,
|
||||
name = historyItem.getString("Ocena") ?: "",
|
||||
type = TYPE_NORMAL,
|
||||
value = value,
|
||||
weight = if (value > 0f && countToTheAverage) weight * -1f else 0f,
|
||||
color = colorInt,
|
||||
category = historyItem.getString("Kategoria"),
|
||||
description = historyItem.getString("Uzasadnienie"),
|
||||
comment = null,
|
||||
semester = historyItem.getInt("Semestr") ?: 1,
|
||||
teacherId = teacher.id,
|
||||
subjectId = subject.id)
|
||||
historyObject.parentId = gradeObject.id
|
||||
|
||||
val addedDate = historyItem.getString("Data_wystaw")?.let { Date.fromY_m_d(it).inMillis } ?: System.currentTimeMillis()
|
||||
|
@ -13,8 +13,8 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_SEMESTER1_PROPOSED
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_YEAR_PROPOSED
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_PROPOSED
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_PROPOSED
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.getJsonArray
|
||||
@ -54,20 +54,20 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
|
||||
|
||||
if (semester1Proposed != "") {
|
||||
val gradeObject = Grade(
|
||||
profileId,
|
||||
semester1Id,
|
||||
"",
|
||||
-1,
|
||||
"",
|
||||
semester1Value.toString(),
|
||||
semester1Value.toFloat(),
|
||||
0f,
|
||||
1,
|
||||
-1,
|
||||
subjectObject.id
|
||||
).apply {
|
||||
type = TYPE_SEMESTER1_PROPOSED
|
||||
}
|
||||
profileId = profileId,
|
||||
id = semester1Id,
|
||||
name = semester1Value.toString(),
|
||||
type = TYPE_SEMESTER1_PROPOSED,
|
||||
value = semester1Value.toFloat(),
|
||||
weight = 0f,
|
||||
color = -1,
|
||||
category = null,
|
||||
description = null,
|
||||
comment = null,
|
||||
semester = 1,
|
||||
teacherId = -1,
|
||||
subjectId = subjectObject.id
|
||||
)
|
||||
|
||||
data.gradeList.add(gradeObject)
|
||||
data.metadataList.add(Metadata(
|
||||
@ -82,20 +82,25 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
|
||||
|
||||
if (semester2Proposed != "") {
|
||||
val gradeObject = Grade(
|
||||
profileId,
|
||||
semester2Id,
|
||||
"",
|
||||
-1,
|
||||
"",
|
||||
semester2Value.toString(),
|
||||
semester2Value.toFloat(),
|
||||
0f,
|
||||
2,
|
||||
-1,
|
||||
subjectObject.id
|
||||
).apply {
|
||||
type = TYPE_YEAR_PROPOSED
|
||||
}
|
||||
profileId = profileId,
|
||||
id = semester2Id,
|
||||
name = semester2Value.toString(),
|
||||
type = TYPE_YEAR_PROPOSED,
|
||||
value = semester2Value.toFloat(),
|
||||
weight = 0f,
|
||||
color = -1,
|
||||
category = null,
|
||||
description = null,
|
||||
comment = null,
|
||||
semester = 2,
|
||||
teacherId = -1,
|
||||
subjectId = subjectObject.id
|
||||
)
|
||||
|
||||
val addedDate = if (data.profile.empty)
|
||||
data.profile.dateSemester1Start.inMillis
|
||||
else
|
||||
System.currentTimeMillis()
|
||||
|
||||
data.gradeList.add(gradeObject)
|
||||
data.metadataList.add(Metadata(
|
||||
@ -104,7 +109,7 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
|
||||
gradeObject.id,
|
||||
profile.empty,
|
||||
profile.empty,
|
||||
System.currentTimeMillis()
|
||||
addedDate
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_HOME
|
||||
import pl.szczodrzynski.edziennik.data.api.Regexes
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
|
||||
import pl.szczodrzynski.edziennik.get
|
||||
import pl.szczodrzynski.edziennik.getString
|
||||
|
||||
class IdziennikWebSwitchRegister(override val data: DataIdziennik,
|
||||
val registerId: Int,
|
||||
val onSuccess: () -> Unit
|
||||
) : IdziennikWeb(data, null) {
|
||||
companion object {
|
||||
private const val TAG = "IdziennikWebSwitchRegister"
|
||||
}
|
||||
|
||||
init {
|
||||
val hiddenFields = data.loginStore.getLoginData("hiddenFields", JsonObject())
|
||||
// TODO error checking
|
||||
|
||||
webGet(TAG, IDZIENNIK_WEB_HOME, mapOf(
|
||||
"__VIEWSTATE" to hiddenFields.getString("__VIEWSTATE", ""),
|
||||
"__VIEWSTATEGENERATOR" to hiddenFields.getString("__VIEWSTATEGENERATOR", ""),
|
||||
"__EVENTVALIDATION" to hiddenFields.getString("__EVENTVALIDATION", ""),
|
||||
"ctl00\$dxComboUczniowie" to registerId
|
||||
)) { text ->
|
||||
Regexes.IDZIENNIK_WEB_SELECTED_REGISTER.find(text)?.let {
|
||||
val registerId = it[1].toIntOrNull() ?: return@let
|
||||
data.webSelectedRegister = registerId
|
||||
}
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
@ -8,14 +8,14 @@ import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||
import okhttp3.Cookie
|
||||
import pl.szczodrzynski.edziennik.HOUR
|
||||
import pl.szczodrzynski.edziennik.MINUTE
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.get
|
||||
import pl.szczodrzynski.edziennik.getUnixDate
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class IdziennikLoginWeb(val data: DataIdziennik, val onSuccess: () -> Unit) {
|
||||
companion object {
|
||||
@ -69,6 +69,40 @@ class IdziennikLoginWeb(val data: DataIdziennik, val onSuccess: () -> Unit) {
|
||||
data.apiBearer = cookies.singleOrNull { it.name() == "Bearer" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_BEARER
|
||||
data.loginExpiryTime = response.getUnixDate() + 30 * MINUTE /* after about 40 minutes the login didn't work already */
|
||||
data.apiExpiryTime = response.getUnixDate() + 12 * HOUR /* actually it expires after 24 hours but I'm not sure when does the token refresh. */
|
||||
|
||||
val hiddenFields = JsonObject()
|
||||
Regexes.IDZIENNIK_LOGIN_HIDDEN_FIELDS.findAll(text).forEach {
|
||||
hiddenFields[it[1]] = it[2]
|
||||
}
|
||||
data.loginStore.putLoginData("hiddenFields", hiddenFields)
|
||||
|
||||
Regexes.IDZIENNIK_WEB_SELECTED_REGISTER.find(text)?.let {
|
||||
val registerId = it[1].toIntOrNull() ?: return@let
|
||||
data.webSelectedRegister = registerId
|
||||
}
|
||||
|
||||
data.profile?.let { profile ->
|
||||
Regexes.IDZIENNIK_WEB_LUCKY_NUMBER.find(text)?.also {
|
||||
val number = it[1].toIntOrNull() ?: return@also
|
||||
val luckyNumberObject = LuckyNumber(
|
||||
data.profileId,
|
||||
Date.getToday(),
|
||||
number
|
||||
)
|
||||
|
||||
data.luckyNumberList.add(luckyNumberObject)
|
||||
data.metadataList.add(
|
||||
Metadata(
|
||||
profile.id,
|
||||
Metadata.TYPE_LUCKY_NUMBER,
|
||||
luckyNumberObject.date.value.toLong(),
|
||||
true,
|
||||
profile.empty,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
return@run null
|
||||
}?.let { errorCode ->
|
||||
data.error(ApiError(TAG, errorCode)
|
||||
|
@ -199,7 +199,6 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
ERROR_LOGIN_LIBRUS_MESSAGES_NO_SESSION_ID -> {
|
||||
login()
|
||||
}
|
||||
// TODO PORTAL CAPTCHA
|
||||
ERROR_LIBRUS_API_TIMETABLE_NOT_PUBLIC -> {
|
||||
data.timetableNotPublic = true
|
||||
data()
|
||||
@ -208,6 +207,11 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
ERROR_LIBRUS_API_NOTES_NOT_ACTIVE -> {
|
||||
data()
|
||||
}
|
||||
ERROR_LIBRUS_API_DEVICE_REGISTERED -> {
|
||||
data.app.config.sync.tokenLibrusList =
|
||||
data.app.config.sync.tokenLibrusList + data.profileId
|
||||
data()
|
||||
}
|
||||
else -> callback.onError(apiError)
|
||||
}
|
||||
}
|
||||
|
@ -44,9 +44,12 @@ open class LibrusApi(open val data: DataLibrus, open val lastSync: Long?) {
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
/*
|
||||
{"Status":"Error","Code":"DeviceRegistered","Message":"This device is alerdy registered.","Resources":{"..":{"Url":"https:\/\/api.librus.pl\/2.0\/Root"}},"Url":"https:\/\/api.librus.pl\/2.0\/ChangeRegister"}*/
|
||||
val error = if (response?.code() == 200) null else
|
||||
json.getString("Code") ?:
|
||||
json.getString("Message") ?:
|
||||
json.getString("Status") ?:
|
||||
response?.parserErrorBody
|
||||
error?.let { code ->
|
||||
when (code) {
|
||||
@ -64,6 +67,8 @@ open class LibrusApi(open val data: DataLibrus, open val lastSync: Long?) {
|
||||
"InvalidRequest" -> ERROR_LIBRUS_API_INVALID_REQUEST_PARAMS
|
||||
"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) {
|
||||
@ -115,6 +120,8 @@ open class LibrusApi(open val data: DataLibrus, open val lastSync: Long?) {
|
||||
.allowErrorCode(HTTP_FORBIDDEN)
|
||||
.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()
|
||||
|
@ -10,6 +10,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
|
||||
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_POINT_SUM
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
@ -26,23 +27,36 @@ 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 ->
|
||||
|
||||
if (data.startPointsSemester1 > 0) {
|
||||
val semester1StartGradeObject = Grade(
|
||||
profileId,
|
||||
-101,
|
||||
data.app.getString(R.string.grade_start_points),
|
||||
0xffbdbdbd.toInt(),
|
||||
data.app.getString(R.string.grade_start_points_format, 1),
|
||||
nameFormat.format(data.startPointsSemester1),
|
||||
data.startPointsSemester1.toFloat(),
|
||||
-1f,
|
||||
1,
|
||||
-1,
|
||||
1
|
||||
).apply { type = Grade.TYPE_POINT_SUM }
|
||||
profileId = profileId,
|
||||
id = -101,
|
||||
name = nameFormat.format(data.startPointsSemester1),
|
||||
type = TYPE_POINT_SUM,
|
||||
value = data.startPointsSemester1.toFloat(),
|
||||
weight = 0f,
|
||||
color = 0xffbdbdbd.toInt(),
|
||||
category = data.app.getString(R.string.grade_start_points),
|
||||
description = data.app.getString(R.string.grade_start_points_format, 1),
|
||||
comment = null,
|
||||
semester = 1,
|
||||
teacherId = -1,
|
||||
subjectId = 1
|
||||
)
|
||||
|
||||
data.gradeList.add(semester1StartGradeObject)
|
||||
data.metadataList.add(Metadata(
|
||||
@ -57,18 +71,20 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
|
||||
|
||||
if (data.startPointsSemester2 > 0) {
|
||||
val semester2StartGradeObject = Grade(
|
||||
profileId,
|
||||
-102,
|
||||
data.app.getString(R.string.grade_start_points),
|
||||
0xffbdbdbd.toInt(),
|
||||
data.app.getString(R.string.grade_start_points_format, 2),
|
||||
nameFormat.format(data.startPointsSemester2),
|
||||
data.startPointsSemester2.toFloat(),
|
||||
-1f,
|
||||
2,
|
||||
-1,
|
||||
1
|
||||
).apply { type = Grade.TYPE_POINT_SUM }
|
||||
profileId = profileId,
|
||||
id = -102,
|
||||
name = nameFormat.format(data.startPointsSemester2),
|
||||
type = TYPE_POINT_SUM,
|
||||
value = data.startPointsSemester2.toFloat(),
|
||||
weight = -1f,
|
||||
color = 0xffbdbdbd.toInt(),
|
||||
category = data.app.getString(R.string.grade_start_points),
|
||||
description = data.app.getString(R.string.grade_start_points_format, 2),
|
||||
comment = null,
|
||||
semester = 2,
|
||||
teacherId = -1,
|
||||
subjectId = 1
|
||||
)
|
||||
|
||||
data.gradeList.add(semester2StartGradeObject)
|
||||
data.metadataList.add(Metadata(
|
||||
@ -90,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
|
||||
}
|
||||
@ -110,32 +130,33 @@ 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
|
||||
|
||||
val gradeObject = Grade(
|
||||
profileId,
|
||||
id,
|
||||
categoryName,
|
||||
color,
|
||||
description,
|
||||
name,
|
||||
valueFrom,
|
||||
-1f,
|
||||
semester,
|
||||
teacherId,
|
||||
1
|
||||
profileId = profileId,
|
||||
id = id,
|
||||
name = name,
|
||||
type = TYPE_POINT_SUM,
|
||||
value = valueFrom,
|
||||
weight = -1f,
|
||||
color = color,
|
||||
category = categoryName,
|
||||
description = text ?: description.join(" - "),
|
||||
comment = if (text != null) description.join(" - ") else null,
|
||||
semester = semester,
|
||||
teacherId = teacherId,
|
||||
subjectId = 1
|
||||
).apply {
|
||||
type = Grade.TYPE_POINT_SUM
|
||||
valueMax = valueTo
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,8 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
|
||||
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_DESCRIPTIVE_TEXT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_TEXT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_DESCRIPTIVE_TEXT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_TEXT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
@ -53,20 +53,20 @@ class LibrusApiDescriptiveGrades(override val data: DataLibrus,
|
||||
val addedDate = Date.fromIso(grade.getString("AddDate") ?: return@forEach)
|
||||
|
||||
val gradeObject = Grade(
|
||||
profileId,
|
||||
id,
|
||||
category?.text ?: "",
|
||||
category?.color ?: -1,
|
||||
description,
|
||||
" ",
|
||||
0f,
|
||||
0f,
|
||||
semester,
|
||||
teacherId,
|
||||
subjectId
|
||||
).apply {
|
||||
this.type = type
|
||||
}
|
||||
profileId = profileId,
|
||||
id = id,
|
||||
name = " ",
|
||||
type = type,
|
||||
value = 0f,
|
||||
weight = 0f,
|
||||
color = category?.color ?: -1,
|
||||
category = category?.text,
|
||||
description = description,
|
||||
comment = null,
|
||||
semester = semester,
|
||||
teacherId = teacherId,
|
||||
subjectId = subjectId
|
||||
)
|
||||
|
||||
data.gradeList.add(gradeObject)
|
||||
data.metadataList.add(Metadata(
|
||||
|
@ -6,7 +6,13 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
|
||||
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||
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_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.GradeCategory
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
@ -54,32 +60,28 @@ class LibrusApiGrades(override val data: DataLibrus,
|
||||
} ?: ""
|
||||
|
||||
val gradeObject = Grade(
|
||||
profileId,
|
||||
id,
|
||||
category?.text ?: "",
|
||||
category?.color ?: -1,
|
||||
description,
|
||||
name,
|
||||
value,
|
||||
weight,
|
||||
semester,
|
||||
teacherId,
|
||||
subjectId
|
||||
profileId = profileId,
|
||||
id = id,
|
||||
name = name,
|
||||
type = when {
|
||||
grade.getBoolean("IsConstituent") ?: false -> TYPE_NORMAL
|
||||
grade.getBoolean("IsSemester") ?: false -> if (semester == 1) TYPE_SEMESTER1_FINAL else TYPE_SEMESTER2_FINAL
|
||||
grade.getBoolean("IsSemesterProposition") ?: false -> if (semester == 1) TYPE_SEMESTER1_PROPOSED else TYPE_SEMESTER2_PROPOSED
|
||||
grade.getBoolean("IsFinal") ?: false -> TYPE_YEAR_FINAL
|
||||
grade.getBoolean("IsFinalProposition") ?: false -> TYPE_YEAR_PROPOSED
|
||||
else -> TYPE_NORMAL
|
||||
},
|
||||
value = value,
|
||||
weight = weight,
|
||||
color = category?.color ?: -1,
|
||||
category = category?.text ?: "",
|
||||
description = description,
|
||||
comment = null,
|
||||
semester = semester,
|
||||
teacherId = teacherId,
|
||||
subjectId = subjectId
|
||||
)
|
||||
|
||||
when {
|
||||
grade.getBoolean("IsConstituent") ?: false ->
|
||||
gradeObject.type = TYPE_NORMAL
|
||||
grade.getBoolean("IsSemester") ?: false -> // semester final
|
||||
gradeObject.type = if (gradeObject.semester == 1) TYPE_SEMESTER1_FINAL else TYPE_SEMESTER2_FINAL
|
||||
grade.getBoolean("IsSemesterProposition") ?: false -> // semester proposed
|
||||
gradeObject.type = if (gradeObject.semester == 1) TYPE_SEMESTER1_PROPOSED else TYPE_SEMESTER2_PROPOSED
|
||||
grade.getBoolean("IsFinal") ?: false -> // year final
|
||||
gradeObject.type = TYPE_YEAR_FINAL
|
||||
grade.getBoolean("IsFinalProposition") ?: false -> // year final
|
||||
gradeObject.type = TYPE_YEAR_PROPOSED
|
||||
}
|
||||
|
||||
grade.getJsonObject("Improvement")?.also {
|
||||
val historicalId = it.getLong("Id")
|
||||
data.gradeList.firstOrNull { grade -> grade.id == historicalId }?.also { grade ->
|
||||
|
@ -10,7 +10,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
|
||||
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_POINT_AVG
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_POINT_AVG
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
@ -44,19 +44,20 @@ class LibrusApiPointGrades(override val data: DataLibrus,
|
||||
val addedDate = Date.fromIso(grade.getString("AddDate") ?: return@forEach)
|
||||
|
||||
val gradeObject = Grade(
|
||||
profileId,
|
||||
id,
|
||||
category?.text ?: "",
|
||||
category?.color ?: -1,
|
||||
"",
|
||||
name,
|
||||
value,
|
||||
category?.weight ?: 0f,
|
||||
semester,
|
||||
teacherId,
|
||||
subjectId
|
||||
profileId = profileId,
|
||||
id = id,
|
||||
name = name,
|
||||
type = TYPE_POINT_AVG,
|
||||
value = value,
|
||||
weight = category?.weight ?: 0f,
|
||||
color = category?.color ?: -1,
|
||||
category = category?.text ?: "",
|
||||
description = null,
|
||||
comment = null,
|
||||
semester = semester,
|
||||
teacherId = teacherId,
|
||||
subjectId = subjectId
|
||||
).apply {
|
||||
type = TYPE_POINT_AVG
|
||||
valueMax = category?.valueTo ?: 0f
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
|
||||
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_DESCRIPTIVE
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_DESCRIPTIVE
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
@ -48,20 +48,20 @@ class LibrusApiTextGrades(override val data: DataLibrus,
|
||||
val addedDate = Date.fromIso(grade.getString("AddDate") ?: return@forEach)
|
||||
|
||||
val gradeObject = Grade(
|
||||
profileId,
|
||||
id,
|
||||
category?.text ?: "",
|
||||
category?.color ?: -1,
|
||||
description,
|
||||
name,
|
||||
0f,
|
||||
0f,
|
||||
semester,
|
||||
teacherId,
|
||||
subjectId
|
||||
).apply {
|
||||
type = TYPE_DESCRIPTIVE
|
||||
}
|
||||
profileId = profileId,
|
||||
id = id,
|
||||
name = name,
|
||||
type = TYPE_DESCRIPTIVE,
|
||||
value = 0f,
|
||||
weight = 0f,
|
||||
color = category?.color ?: -1,
|
||||
category = category?.text ?: "",
|
||||
description = description,
|
||||
comment = grade.getString("Phrase") /* whatever it is */,
|
||||
semester = semester,
|
||||
teacherId = teacherId,
|
||||
subjectId = subjectId
|
||||
)
|
||||
|
||||
data.gradeList.add(gradeObject)
|
||||
data.metadataList.add(Metadata(
|
||||
|
@ -144,18 +144,14 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
if (json.getBoolean("captchaRequired") == true) {
|
||||
data.error(ApiError(TAG, ERROR_CAPTCHA_LIBRUS_PORTAL)
|
||||
.withResponse(response)
|
||||
.withApiResponse(json))
|
||||
return
|
||||
}
|
||||
val error = if (response.code() == 200) null else
|
||||
json.getJsonArray("errors")?.getString(0)
|
||||
error?.let { code ->
|
||||
when {
|
||||
code.contains("Sesja logowania wygasła") -> ERROR_LOGIN_LIBRUS_PORTAL_CSRF_EXPIRED
|
||||
code.contains("Upewnij się, że nie") -> ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN
|
||||
// this doesn't work anyway: `errors` is an object with `g-recaptcha-response` set
|
||||
code.contains("robotem") -> ERROR_CAPTCHA_LIBRUS_PORTAL
|
||||
else -> ERROR_LOGIN_LIBRUS_PORTAL_ACTION_ERROR
|
||||
}.let { errorCode ->
|
||||
data.error(ApiError(TAG, errorCode)
|
||||
@ -164,6 +160,12 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if (json.getBoolean("captchaRequired") == true) {
|
||||
data.error(ApiError(TAG, ERROR_CAPTCHA_LIBRUS_PORTAL)
|
||||
.withResponse(response)
|
||||
.withApiResponse(json))
|
||||
return
|
||||
}
|
||||
authorize(json.getString("redirect", LIBRUS_AUTHORIZE_URL))
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,13 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.api
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||
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_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.Metadata
|
||||
|
||||
class MobidziennikApiGrades(val data: DataMobidziennik, rows: List<String>) {
|
||||
@ -61,18 +67,23 @@ class MobidziennikApiGrades(val data: DataMobidziennik, rows: List<String>) {
|
||||
}
|
||||
|
||||
val gradeObject = Grade(
|
||||
data.profileId,
|
||||
id,
|
||||
category,
|
||||
color,
|
||||
description,
|
||||
name,
|
||||
value,
|
||||
weight,
|
||||
semester,
|
||||
teacherId,
|
||||
subjectId)
|
||||
gradeObject.type = type
|
||||
profileId = data.profileId,
|
||||
id = id,
|
||||
name = name,
|
||||
type = type,
|
||||
value = value,
|
||||
weight = weight,
|
||||
color = color,
|
||||
category = category,
|
||||
description = description,
|
||||
comment = null,
|
||||
semester = semester,
|
||||
teacherId = teacherId,
|
||||
subjectId = subjectId)
|
||||
|
||||
if (data.profile?.empty == true) {
|
||||
addedDate = data.profile.dateSemester1Start.inMillis
|
||||
}
|
||||
|
||||
data.gradeList.add(gradeObject)
|
||||
data.metadataList.add(
|
||||
|
@ -11,6 +11,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidzienn
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_GRADES
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
|
||||
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.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.fixWhiteSpaces
|
||||
@ -112,17 +113,19 @@ class MobidziennikWebGrades(override val data: DataMobidziennik,
|
||||
}
|
||||
|
||||
val gradeObject = Grade(
|
||||
profileId,
|
||||
gradeId,
|
||||
gradeCategory,
|
||||
gradeColor,
|
||||
"NLDŚR, $gradeDescription",
|
||||
gradeName,
|
||||
gradeValue,
|
||||
0f,
|
||||
gradeSemester,
|
||||
teacherId,
|
||||
subjectId
|
||||
profileId = profileId,
|
||||
id = gradeId,
|
||||
name = gradeName,
|
||||
type = TYPE_NORMAL,
|
||||
value = gradeValue,
|
||||
weight = 0f,
|
||||
color = gradeColor,
|
||||
category = gradeCategory,
|
||||
description = "NLDŚR, $gradeDescription",
|
||||
comment = null,
|
||||
semester = gradeSemester,
|
||||
teacherId = teacherId,
|
||||
subjectId = subjectId
|
||||
)
|
||||
|
||||
gradeObject.classAverage = gradeClassAverage
|
||||
|
@ -11,6 +11,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_API_
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
|
||||
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
|
||||
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.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import java.text.DecimalFormat
|
||||
@ -88,17 +89,19 @@ class VulcanApiGrades(override val data: DataVulcan,
|
||||
}.toInt()
|
||||
|
||||
val gradeObject = Grade(
|
||||
profileId,
|
||||
id,
|
||||
category,
|
||||
color,
|
||||
finalDescription,
|
||||
name,
|
||||
value ?: 0.0f,
|
||||
weight,
|
||||
data.studentSemesterNumber,
|
||||
teacherId,
|
||||
subjectId
|
||||
profileId = profileId,
|
||||
id = id,
|
||||
name = name,
|
||||
type = TYPE_NORMAL,
|
||||
value = value ?: 0.0f,
|
||||
weight = weight,
|
||||
color = color,
|
||||
category = category,
|
||||
description = finalDescription,
|
||||
comment = null,
|
||||
semester = data.studentSemesterNumber,
|
||||
teacherId = teacherId,
|
||||
subjectId = subjectId
|
||||
)
|
||||
|
||||
data.gradeList.add(gradeObject)
|
||||
|
@ -8,6 +8,10 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_API_GRADES_SUMMARY
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||
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.Metadata
|
||||
import pl.szczodrzynski.edziennik.getJsonArray
|
||||
import pl.szczodrzynski.edziennik.getJsonObject
|
||||
@ -53,23 +57,25 @@ class VulcanApiProposedGrades(override val data: DataVulcan,
|
||||
val color = Utils.getVulcanGradeColor(name)
|
||||
|
||||
val gradeObject = Grade(
|
||||
profileId,
|
||||
id,
|
||||
"",
|
||||
color,
|
||||
"",
|
||||
name,
|
||||
value,
|
||||
0f,
|
||||
data.studentSemesterNumber,
|
||||
-1,
|
||||
subjectId
|
||||
profileId = profileId,
|
||||
id = id,
|
||||
name = name,
|
||||
type = if (data.studentSemesterNumber == 1) {
|
||||
if (isFinal) TYPE_SEMESTER1_FINAL else TYPE_SEMESTER1_PROPOSED
|
||||
} else {
|
||||
if (isFinal) TYPE_SEMESTER2_FINAL else TYPE_SEMESTER2_PROPOSED
|
||||
},
|
||||
value = value,
|
||||
weight = 0f,
|
||||
color = color,
|
||||
category = "",
|
||||
description = null,
|
||||
comment = null,
|
||||
semester = data.studentSemesterNumber,
|
||||
teacherId = -1,
|
||||
subjectId = subjectId
|
||||
)
|
||||
if (data.studentSemesterNumber == 1) {
|
||||
gradeObject.type = if (isFinal) Grade.TYPE_SEMESTER1_FINAL else Grade.TYPE_SEMESTER1_PROPOSED
|
||||
} else {
|
||||
gradeObject.type = if (isFinal) Grade.TYPE_SEMESTER2_FINAL else Grade.TYPE_SEMESTER2_PROPOSED
|
||||
}
|
||||
|
||||
data.gradeList.add(gradeObject)
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
|
@ -46,6 +46,6 @@ object Signing {
|
||||
|
||||
/*fun provideKey(param1: String, param2: Long): ByteArray {*/
|
||||
fun pleaseStopRightNow(param1: String, param2: Long): ByteArray {
|
||||
return "$param1.MTIzNDU2Nzg5MDp+2J6OAn===.$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");
|
||||
}
|
||||
//Log.d(TAG, "Start is "+start+", now is "+now+", end is "+end);
|
||||
return start > 0 && now >= start && now <= end;
|
||||
}*/
|
||||
fun shouldBeQuiet() = false
|
||||
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)
|
||||
}
|
||||
|
||||
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 = 77)
|
||||
], 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 {
|
||||
@ -160,7 +162,9 @@ abstract class AppDb : RoomDatabase() {
|
||||
Migration74(),
|
||||
Migration75(),
|
||||
Migration76(),
|
||||
Migration77()
|
||||
Migration77(),
|
||||
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
|
||||
|
@ -61,7 +61,7 @@ public abstract class EventDao {
|
||||
|
||||
@RawQuery(observedEntities = {Event.class})
|
||||
abstract LiveData<List<EventFull>> getAll(SupportSQLiteQuery query);
|
||||
public LiveData<List<EventFull>> getAll(int profileId, String filter) {
|
||||
public LiveData<List<EventFull>> getAll(int profileId, String filter, String limit) {
|
||||
String query = "SELECT \n" +
|
||||
"*, \n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName,\n" +
|
||||
@ -75,24 +75,24 @@ public abstract class EventDao {
|
||||
"LEFT JOIN metadata ON eventId = thingId AND (thingType = " + TYPE_EVENT + " OR thingType = " + TYPE_HOMEWORK + ") AND metadata.profileId = "+profileId+"\n" +
|
||||
"WHERE events.profileId = "+profileId+" AND events.eventBlacklisted = 0 AND "+filter+"\n" +
|
||||
"GROUP BY eventId\n" +
|
||||
"ORDER BY eventDate, eventStartTime ASC";
|
||||
"ORDER BY eventDate, eventStartTime ASC "+limit;
|
||||
Log.d("DB", query);
|
||||
return getAll(new SimpleSQLiteQuery(query));
|
||||
}
|
||||
public LiveData<List<EventFull>> getAll(int profileId) {
|
||||
return getAll(profileId, "1");
|
||||
return getAll(profileId, "1", "");
|
||||
}
|
||||
public List<EventFull> getAllNow(int profileId) {
|
||||
return getAllNow(profileId, "1");
|
||||
}
|
||||
public LiveData<List<EventFull>> getAllWhere(int profileId, String filter) {
|
||||
return getAll(profileId, filter);
|
||||
return getAll(profileId, filter, "");
|
||||
}
|
||||
public LiveData<List<EventFull>> getAllByType(int profileId, long type, String filter) {
|
||||
return getAll(profileId, "eventType = "+type+" AND "+filter);
|
||||
return getAll(profileId, "eventType = "+type+" AND "+filter, "");
|
||||
}
|
||||
public LiveData<List<EventFull>> getAllByDate(int profileId, @NonNull Date date) {
|
||||
return getAll(profileId, "eventDate = '"+date.getStringY_m_d()+"'");
|
||||
return getAll(profileId, "eventDate = '"+date.getStringY_m_d()+"'", "");
|
||||
}
|
||||
public List<EventFull> getAllByDateNow(int profileId, @NonNull Date date) {
|
||||
return getAllNow(profileId, "eventDate = '"+date.getStringY_m_d()+"'");
|
||||
@ -100,7 +100,10 @@ public abstract class EventDao {
|
||||
public LiveData<List<EventFull>> getAllByDateTime(int profileId, @NonNull Date date, Time time) {
|
||||
if (time == null)
|
||||
return getAllByDate(profileId, date);
|
||||
return getAll(profileId, "eventDate = '"+date.getStringY_m_d()+"' AND eventStartTime = '"+time.getStringValue()+"'");
|
||||
return getAll(profileId, "eventDate = '"+date.getStringY_m_d()+"' AND eventStartTime = '"+time.getStringValue()+"'", "");
|
||||
}
|
||||
public LiveData<List<EventFull>> getAllNearest(int profileId, @NonNull Date today, int limit) {
|
||||
return getAll(profileId, "eventDate >= '"+today.getStringY_m_d()+"'", "LIMIT "+limit);
|
||||
}
|
||||
|
||||
@RawQuery
|
||||
|
@ -63,8 +63,8 @@ public abstract class MetadataDao {
|
||||
@Transaction
|
||||
public void setSeen(int profileId, Object o, boolean seen) {
|
||||
if (o instanceof Grade) {
|
||||
if (add(new Metadata(profileId, TYPE_GRADE, ((Grade) o).id, seen, false, 0)) == -1) {
|
||||
updateSeen(profileId, TYPE_GRADE, ((Grade) o).id, seen);
|
||||
if (add(new Metadata(profileId, TYPE_GRADE, ((Grade) o).getId(), seen, false, 0)) == -1) {
|
||||
updateSeen(profileId, TYPE_GRADE, ((Grade) o).getId(), seen);
|
||||
}
|
||||
}
|
||||
if (o instanceof Attendance) {
|
||||
@ -102,8 +102,8 @@ public abstract class MetadataDao {
|
||||
@Transaction
|
||||
public void setNotified(int profileId, Object o, boolean notified) {
|
||||
if (o instanceof Grade) {
|
||||
if (add(new Metadata(profileId, TYPE_GRADE, ((Grade) o).id, false, notified, 0)) == -1) {
|
||||
updateNotified(profileId, TYPE_GRADE, ((Grade) o).id, notified);
|
||||
if (add(new Metadata(profileId, TYPE_GRADE, ((Grade) o).getId(), false, notified, 0)) == -1) {
|
||||
updateNotified(profileId, TYPE_GRADE, ((Grade) o).getId(), notified);
|
||||
}
|
||||
}
|
||||
if (o instanceof Attendance) {
|
||||
|
@ -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)
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.entity;
|
||||
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Ignore;
|
||||
import androidx.room.Index;
|
||||
|
||||
@Entity(tableName = "grades",
|
||||
primaryKeys = {"profileId", "gradeId"},
|
||||
indices = {@Index(value = {"profileId"})})
|
||||
public class Grade {
|
||||
public int profileId;
|
||||
|
||||
@ColumnInfo(name = "gradeId")
|
||||
public long id;
|
||||
|
||||
@ColumnInfo(name = "gradeCategory")
|
||||
public String category;
|
||||
@ColumnInfo(name = "gradeColor")
|
||||
public int color;
|
||||
@ColumnInfo(name = "gradeDescription")
|
||||
public String description;
|
||||
@ColumnInfo(name = "gradeComment")
|
||||
public String comment;
|
||||
@ColumnInfo(name = "gradeName")
|
||||
public String name;
|
||||
@ColumnInfo(name = "gradeValue")
|
||||
public float value;
|
||||
@ColumnInfo(name = "gradeValueMax")
|
||||
public float valueMax;
|
||||
@ColumnInfo(name = "gradeWeight")
|
||||
public float weight;
|
||||
@ColumnInfo(name = "gradeSemester")
|
||||
public int semester;
|
||||
@ColumnInfo(name = "gradeClassAverage")
|
||||
public float classAverage = -1;
|
||||
public static final int TYPE_NORMAL = 0;
|
||||
public static final int TYPE_SEMESTER1_PROPOSED = 1;
|
||||
public static final int TYPE_SEMESTER1_FINAL = 2;
|
||||
public static final int TYPE_SEMESTER2_PROPOSED = 3;
|
||||
public static final int TYPE_SEMESTER2_FINAL = 4;
|
||||
public static final int TYPE_YEAR_PROPOSED = 5;
|
||||
public static final int TYPE_YEAR_FINAL = 6;
|
||||
public static final int TYPE_POINT_AVG = 10;
|
||||
public static final int TYPE_POINT_SUM = 20;
|
||||
public static final int TYPE_DESCRIPTIVE = 30;
|
||||
public static final int TYPE_DESCRIPTIVE_TEXT = 31;
|
||||
public static final int TYPE_TEXT = 40;
|
||||
@ColumnInfo(name = "gradeType")
|
||||
public int type = TYPE_NORMAL;
|
||||
@ColumnInfo(name = "gradePointGrade")
|
||||
public boolean pointGrade = false;
|
||||
|
||||
/**
|
||||
* Applies for historical grades. It's the new/replacement grade's ID.
|
||||
*/
|
||||
@ColumnInfo(name = "gradeParentId")
|
||||
public long parentId = -1;
|
||||
|
||||
/**
|
||||
* Applies for current grades. If the grade was worse and this is the improved one.
|
||||
*/
|
||||
@ColumnInfo(name = "gradeIsImprovement")
|
||||
public boolean isImprovement = false;
|
||||
|
||||
public long teacherId;
|
||||
public long subjectId;
|
||||
|
||||
@Ignore
|
||||
public Grade() {}
|
||||
|
||||
public Grade(int profileId, long id, String category, int color, String description, String name, float value, float weight, int semester, long teacherId, long subjectId) {
|
||||
this.profileId = profileId;
|
||||
this.id = id;
|
||||
this.category = category;
|
||||
this.color = color;
|
||||
this.description = description;
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.weight = weight;
|
||||
this.semester = semester;
|
||||
this.teacherId = teacherId;
|
||||
this.subjectId = subjectId;
|
||||
}
|
||||
|
||||
/*@Ignore
|
||||
public Grade(int profileId, long id, String description, String name, float value, float weight, int semester, long teacherId, long categoryId, long subjectId) {
|
||||
this.profileId = profileId;
|
||||
this.id = id;
|
||||
this.description = description;
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.weight = weight;
|
||||
this.semester = semester;
|
||||
this.teacherId = teacherId;
|
||||
//this.categoryId = categoryId;
|
||||
this.subjectId = subjectId;
|
||||
}*/
|
||||
}
|
||||
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
|
||||
*/
|
||||
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) {
|
||||
this.profileId = profileId;
|
||||
this.id = id;
|
||||
this.category = category;
|
||||
this.color = color;
|
||||
this.description = description;
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.weight = weight;
|
||||
this.semester = semester;
|
||||
this.teacherId = teacherId;
|
||||
this.subjectId = subjectId;
|
||||
}*/
|
||||
|
||||
@Entity(tableName = "grades",
|
||||
primaryKeys = ["profileId", "gradeId"],
|
||||
indices = [Index(value = ["profileId"])])
|
||||
open class Grade(
|
||||
val profileId: Int,
|
||||
@ColumnInfo(name = "gradeId")
|
||||
val id: Long,
|
||||
@ColumnInfo(name = "gradeName")
|
||||
var name: String,
|
||||
@ColumnInfo(name = "gradeType")
|
||||
var type: Int,
|
||||
|
||||
@ColumnInfo(name = "gradeValue")
|
||||
var value: Float,
|
||||
@ColumnInfo(name = "gradeWeight")
|
||||
var weight: Float,
|
||||
@ColumnInfo(name = "gradeColor")
|
||||
var color: Int,
|
||||
@ColumnInfo(name = "gradeCategory")
|
||||
var category: String?,
|
||||
@ColumnInfo(name = "gradeDescription")
|
||||
var description: String?,
|
||||
@ColumnInfo(name = "gradeComment")
|
||||
var comment: String?,
|
||||
|
||||
@ColumnInfo(name = "gradeSemester")
|
||||
val semester: Int,
|
||||
val teacherId: Long,
|
||||
val subjectId: Long
|
||||
) {
|
||||
companion object {
|
||||
const val TYPE_NORMAL = 0
|
||||
const val TYPE_SEMESTER1_PROPOSED = 1
|
||||
const val TYPE_SEMESTER1_FINAL = 2
|
||||
const val TYPE_SEMESTER2_PROPOSED = 3
|
||||
const val TYPE_SEMESTER2_FINAL = 4
|
||||
const val TYPE_YEAR_PROPOSED = 5
|
||||
const val TYPE_YEAR_FINAL = 6
|
||||
const val TYPE_POINT_AVG = 10
|
||||
const val TYPE_POINT_SUM = 20
|
||||
const val TYPE_DESCRIPTIVE = 30
|
||||
const val TYPE_DESCRIPTIVE_TEXT = 31
|
||||
const val TYPE_TEXT = 40
|
||||
}
|
||||
|
||||
@ColumnInfo(name = "gradeValueMax")
|
||||
var valueMax: Float? = null
|
||||
@ColumnInfo(name = "gradeClassAverage")
|
||||
var classAverage: Float? = null
|
||||
|
||||
/**
|
||||
* Applies for historical grades. It's the new/replacement grade's ID.
|
||||
*/
|
||||
@ColumnInfo(name = "gradeParentId")
|
||||
var parentId: Long? = null
|
||||
/**
|
||||
* Applies for current grades. If the grade was worse and this is the improved one.
|
||||
*/
|
||||
@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, " +
|
||||
|
@ -43,10 +43,12 @@ class LoginStore(
|
||||
fun getLoginData(key: String, defaultValue: Long) = data.getLong(key) ?: defaultValue
|
||||
fun getLoginData(key: String, defaultValue: Float) = data.getFloat(key) ?: defaultValue
|
||||
fun getLoginData(key: String, defaultValue: Char) = data.getChar(key) ?: defaultValue
|
||||
fun getLoginData(key: String, defaultValue: JsonObject) = data.getJsonObject(key) ?: defaultValue
|
||||
fun putLoginData(key: String, value: Boolean) { data[key] = value }
|
||||
fun putLoginData(key: String, value: String?) { data[key] = value }
|
||||
fun putLoginData(key: String, value: Number) { data[key] = value }
|
||||
fun putLoginData(key: String, value: Char) { data[key] = value }
|
||||
fun putLoginData(key: String, value: JsonObject) { data[key] = value }
|
||||
fun removeLoginData(key: String) { data.remove(key) }
|
||||
|
||||
fun copyFrom(args: Bundle) {
|
||||
|
@ -53,15 +53,8 @@ open class Profile(
|
||||
const val REGISTRATION_UNSPECIFIED = 0
|
||||
const val REGISTRATION_DISABLED = 1
|
||||
const val REGISTRATION_ENABLED = 2
|
||||
const val COLOR_MODE_DEFAULT = 0
|
||||
const val COLOR_MODE_WEIGHTED = 1
|
||||
const val AGENDA_DEFAULT = 0
|
||||
const val AGENDA_CALENDAR = 1
|
||||
const val YEAR_1_AVG_2_AVG = 0
|
||||
const val YEAR_1_SEM_2_AVG = 1
|
||||
const val YEAR_1_AVG_2_SEM = 2
|
||||
const val YEAR_1_SEM_2_SEM = 3
|
||||
const val YEAR_ALL_GRADES = 4
|
||||
}
|
||||
|
||||
override var image: String? = null
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.full;
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade;
|
||||
|
||||
public class GradeFull extends Grade {
|
||||
//public String category = "";
|
||||
//public int color;
|
||||
|
||||
public String subjectLongName = "";
|
||||
public String subjectShortName = "";
|
||||
|
||||
public String teacherFullName = "";
|
||||
|
||||
// metadata
|
||||
public boolean seen;
|
||||
public boolean notified;
|
||||
public long addedDate;
|
||||
}
|
||||
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
|
||||
*/
|
||||
package pl.szczodrzynski.edziennik.data.db.full
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||
|
||||
class GradeFull(
|
||||
profileId: Int, id: Long, name: String, type: Int,
|
||||
value: Float, weight: Float, color: Int,
|
||||
category: String?, description: String?, comment: String?,
|
||||
semester: Int, teacherId: Long, subjectId: Long
|
||||
) : Grade(
|
||||
profileId, id, name, type,
|
||||
value, weight, color,
|
||||
category, description, comment,
|
||||
semester, teacherId, subjectId
|
||||
) {
|
||||
var subjectLongName: String? = null
|
||||
var subjectShortName: String? = null
|
||||
var teacherFullName: String? = null
|
||||
// metadata
|
||||
var seen = false
|
||||
var notified = false
|
||||
var addedDate: Long = 0
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.migration
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration78 : Migration(77, 78) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// grades migration to kotlin
|
||||
database.execSQL("ALTER TABLE grades RENAME TO _grades;")
|
||||
database.execSQL("""CREATE TABLE grades (
|
||||
profileId INTEGER NOT NULL,
|
||||
gradeId INTEGER NOT NULL,
|
||||
gradeName TEXT NOT NULL,
|
||||
gradeType INTEGER NOT NULL,
|
||||
gradeValue REAL NOT NULL,
|
||||
gradeWeight REAL NOT NULL,
|
||||
gradeColor INTEGER NOT NULL,
|
||||
gradeCategory TEXT,
|
||||
gradeDescription TEXT,
|
||||
gradeComment TEXT,
|
||||
gradeSemester INTEGER NOT NULL,
|
||||
teacherId INTEGER NOT NULL,
|
||||
subjectId INTEGER NOT NULL,
|
||||
gradeValueMax REAL DEFAULT NULL,
|
||||
gradeClassAverage REAL DEFAULT NULL,
|
||||
gradeParentId INTEGER DEFAULT NULL,
|
||||
gradeIsImprovement INTEGER NOT NULL,
|
||||
PRIMARY KEY(profileId, gradeId)
|
||||
);""")
|
||||
database.execSQL("DROP INDEX IF EXISTS index_grades_profileId;")
|
||||
database.execSQL("CREATE INDEX index_grades_profileId ON grades (profileId);")
|
||||
database.execSQL("""INSERT INTO grades (profileId, gradeId, gradeName, gradeType, gradeValue, gradeWeight, gradeColor, gradeCategory, gradeDescription, gradeComment, gradeSemester, teacherId, subjectId, gradeValueMax, gradeClassAverage, gradeParentId, gradeIsImprovement)
|
||||
SELECT profileId, gradeId, gradeName, gradeType, gradeValue, gradeWeight, gradeColor,
|
||||
CASE gradeCategory WHEN '' THEN NULL WHEN ' ' THEN NULL ELSE gradeCategory END,
|
||||
CASE gradeDescription WHEN '' THEN NULL WHEN ' ' THEN NULL ELSE gradeDescription END,
|
||||
CASE gradeComment WHEN '' THEN NULL WHEN ' ' THEN NULL ELSE gradeComment END,
|
||||
gradeSemester, teacherId, subjectId,
|
||||
CASE gradeValueMax WHEN 0 THEN NULL ELSE gradeValueMax END,
|
||||
CASE gradeClassAverage WHEN -1 THEN NULL ELSE gradeClassAverage END,
|
||||
CASE gradeParentId WHEN -1 THEN NULL ELSE gradeParentId END,
|
||||
gradeIsImprovement FROM _grades;""")
|
||||
database.execSQL("DROP TABLE _grades;")
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -18,6 +18,7 @@ import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
import pl.szczodrzynski.edziennik.databinding.DialogEventDetailsBinding
|
||||
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
@ -118,9 +119,43 @@ class EventDetailsDialog(
|
||||
)
|
||||
}
|
||||
|
||||
b.goToTimetableButton.setOnClickListener {
|
||||
dialog.dismiss()
|
||||
val dateStr = event.eventDate?.stringY_m_d ?: return@setOnClickListener
|
||||
|
||||
val intent =
|
||||
if (activity is MainActivity && activity.navTargetId == MainActivity.DRAWER_ITEM_TIMETABLE)
|
||||
Intent(TimetableFragment.ACTION_SCROLL_TO_DATE)
|
||||
else if (activity is MainActivity)
|
||||
Intent("android.intent.action.MAIN")
|
||||
else
|
||||
Intent(activity, MainActivity::class.java)
|
||||
|
||||
intent.apply {
|
||||
putExtra("fragmentId", MainActivity.DRAWER_ITEM_TIMETABLE)
|
||||
putExtra("timetableDate", dateStr)
|
||||
}
|
||||
if (activity is MainActivity)
|
||||
activity.sendBroadcast(intent)
|
||||
else
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
b.saveInCalendarButton.setOnClickListener {
|
||||
openInCalendar()
|
||||
}
|
||||
|
||||
b.goToTimetableButton.setOnLongClickListener {
|
||||
Toast.makeText(activity, R.string.hint_go_to_timetable, Toast.LENGTH_SHORT).show()
|
||||
true
|
||||
}
|
||||
b.saveInCalendarButton.setOnLongClickListener {
|
||||
Toast.makeText(activity, R.string.hint_save_in_calendar, Toast.LENGTH_SHORT).show()
|
||||
true
|
||||
}
|
||||
b.editButton.setOnLongClickListener {
|
||||
Toast.makeText(activity, R.string.hint_edit_event, Toast.LENGTH_SHORT).show()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun showRemoveEventDialog() {
|
||||
|
@ -8,14 +8,19 @@ import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
import pl.szczodrzynski.edziennik.databinding.EventListItemBinding
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||
|
||||
class EventListAdapter(
|
||||
val context: Context,
|
||||
val simpleMode: Boolean = false,
|
||||
val showDate: Boolean = false,
|
||||
val showWeekDay: Boolean = false,
|
||||
val onItemClick: ((event: EventFull) -> Unit)? = null,
|
||||
val onEventEditClick: ((event: EventFull) -> Unit)? = null
|
||||
) : RecyclerView.Adapter<EventListAdapter.ViewHolder>() {
|
||||
@ -40,12 +45,16 @@ class EventListAdapter(
|
||||
|
||||
val bullet = " • "
|
||||
|
||||
b.simpleMode = simpleMode
|
||||
|
||||
b.topic.text = event.topic
|
||||
|
||||
b.details.text = mutableListOf<CharSequence?>(
|
||||
if (showWeekDay) Week.getFullDayName(event.eventDate.weekDay) else null,
|
||||
if (showDate) event.eventDate.getRelativeString(context, 7) ?: event.eventDate.formattedStringShort else null,
|
||||
event.typeName,
|
||||
event.startTime?.stringHM ?: app.getString(R.string.event_all_day),
|
||||
event.subjectLongName
|
||||
if (simpleMode) null else event.startTime?.stringHM ?: app.getString(R.string.event_all_day),
|
||||
if (simpleMode) null else event.subjectLongName
|
||||
).concat(bullet)
|
||||
|
||||
b.addedBy.setText(
|
||||
@ -65,11 +74,16 @@ class EventListAdapter(
|
||||
|
||||
b.typeColor.background?.setTintColor(event.getColor())
|
||||
|
||||
b.editButton.visibility = if (event.addedManually) View.VISIBLE else View.GONE
|
||||
b.editButton.visibility = if (event.addedManually && !simpleMode) View.VISIBLE else View.GONE
|
||||
b.editButton.onClick {
|
||||
onEventEditClick?.invoke(event)
|
||||
}
|
||||
|
||||
b.editButton.setOnLongClickListener {
|
||||
Toast.makeText(context, R.string.hint_edit_event, Toast.LENGTH_SHORT).show()
|
||||
true
|
||||
}
|
||||
|
||||
/*with(holder) {
|
||||
b.eventListItemRoot.background.colorFilter = when (event.type) {
|
||||
Event.TYPE_HOMEWORK -> PorterDuffColorFilter(0xffffffff.toInt(), PorterDuff.Mode.CLEAR)
|
||||
|
@ -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
|
||||
|
@ -1,129 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.ui.dialogs.grade;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.os.AsyncTask;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.List;
|
||||
|
||||
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.databinding.DialogGradeDetailsBinding;
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesListAdapter;
|
||||
import pl.szczodrzynski.edziennik.utils.Colors;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.COLOR_MODE_DEFAULT;
|
||||
|
||||
public class GradeDetailsDialog {
|
||||
private App app;
|
||||
private Context context;
|
||||
private int profileId;
|
||||
|
||||
public GradeDetailsDialog(Context context) {
|
||||
this.context = context;
|
||||
this.profileId = App.Companion.getProfileId();
|
||||
}
|
||||
public GradeDetailsDialog(Context context, int profileId) {
|
||||
this.context = context;
|
||||
this.profileId = profileId;
|
||||
}
|
||||
|
||||
public MaterialDialog dialog;
|
||||
private DialogGradeDetailsBinding b;
|
||||
private DialogInterface.OnDismissListener dismissListener;
|
||||
public boolean callDismissListener = true;
|
||||
|
||||
public GradeDetailsDialog withDismissListener(DialogInterface.OnDismissListener dismissListener) {
|
||||
this.dismissListener = dismissListener;
|
||||
return this;
|
||||
}
|
||||
public void performDismiss(DialogInterface dialogInterface) {
|
||||
if (callDismissListener && dismissListener != null) {
|
||||
dismissListener.onDismiss(dialogInterface);
|
||||
}
|
||||
callDismissListener = true;
|
||||
}
|
||||
|
||||
public void show(App _app, GradeFull grade)
|
||||
{
|
||||
this.app = _app;
|
||||
dialog = new MaterialDialog.Builder(context)
|
||||
.customView(R.layout.dialog_grade_details, true)
|
||||
.positiveText(R.string.close)
|
||||
.autoDismiss(false)
|
||||
.onPositive((dialog, which) -> dialog.dismiss())
|
||||
.dismissListener(this::performDismiss)
|
||||
.show();
|
||||
|
||||
View root = dialog.getCustomView();
|
||||
assert root != null;
|
||||
|
||||
b = DialogGradeDetailsBinding.bind(root);
|
||||
|
||||
b.setGrade(grade);
|
||||
|
||||
int gradeColor;
|
||||
if (App.Companion.getConfig().getFor(profileId).getGrades().getColorMode() == COLOR_MODE_DEFAULT) {
|
||||
gradeColor = grade.color;
|
||||
}
|
||||
else {
|
||||
gradeColor = Colors.gradeToColor(grade);
|
||||
}
|
||||
|
||||
DecimalFormat format = new DecimalFormat("#.##");
|
||||
if (grade.weight < 0) {
|
||||
grade.weight *= -1;
|
||||
}
|
||||
if (grade.type == Grade.TYPE_DESCRIPTIVE || grade.type == Grade.TYPE_DESCRIPTIVE_TEXT || grade.type == Grade.TYPE_TEXT || grade.type == Grade.TYPE_POINT_SUM) {
|
||||
b.setWeightText(null);
|
||||
grade.weight = 0;
|
||||
}
|
||||
else {
|
||||
if (grade.type == Grade.TYPE_POINT_AVG) {
|
||||
b.setWeightText(app.getString(R.string.grades_max_points_format, format.format(grade.valueMax)));
|
||||
}
|
||||
else if (grade.weight == 0) {
|
||||
b.setWeightText(app.getString(R.string.grades_weight_not_counted));
|
||||
}
|
||||
else {
|
||||
b.setWeightText(app.getString(R.string.grades_weight_format, format.format(grade.weight)));
|
||||
}
|
||||
}
|
||||
|
||||
b.setCommentVisible(false);
|
||||
|
||||
b.setDevMode(App.Companion.getDevMode());
|
||||
|
||||
b.gradeName.setTextColor(ColorUtils.calculateLuminance(gradeColor) > 0.25 ? 0xff000000 : 0xffffffff);
|
||||
b.gradeName.getBackground().setColorFilter(new PorterDuffColorFilter(gradeColor, PorterDuff.Mode.MULTIPLY));
|
||||
|
||||
AsyncTask.execute(() -> {
|
||||
|
||||
List<GradeFull> historyList = app.db.gradeDao().getAllWithParentIdNow(profileId, grade.id);
|
||||
|
||||
if (historyList.size() == 0) {
|
||||
b.setHistoryVisible(false);
|
||||
return;
|
||||
}
|
||||
b.setHistoryVisible(true);
|
||||
b.gradeHistoryNest.post(() -> {
|
||||
b.gradeHistoryNest.setNestedScrollingEnabled(false);
|
||||
b.gradeHistoryList.setHasFixedSize(false);
|
||||
b.gradeHistoryList.setNestedScrollingEnabled(false);
|
||||
b.gradeHistoryList.setLayoutManager(new LinearLayoutManager(context));
|
||||
b.gradeHistoryList.setAdapter(new GradesListAdapter(context, historyList));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
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.*
|
||||
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
|
||||
|
||||
class GradeDetailsDialog(
|
||||
val activity: AppCompatActivity,
|
||||
val grade: GradeFull,
|
||||
val onShowListener: ((tag: String) -> Unit)? = null,
|
||||
val onDismissListener: ((tag: String) -> Unit)? = null
|
||||
) : CoroutineScope {
|
||||
companion object {
|
||||
private const val TAG = "GradeDetailsDialog"
|
||||
}
|
||||
|
||||
private lateinit var app: App
|
||||
private lateinit var b: DialogGradeDetailsBinding
|
||||
private lateinit var dialog: AlertDialog
|
||||
|
||||
private val job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
// local variables go here
|
||||
|
||||
init { run {
|
||||
if (activity.isFinishing)
|
||||
return@run
|
||||
onShowListener?.invoke(TAG)
|
||||
app = activity.applicationContext as App
|
||||
b = DialogGradeDetailsBinding.inflate(activity.layoutInflater)
|
||||
dialog = MaterialAlertDialogBuilder(activity)
|
||||
.setView(b.root)
|
||||
.setPositiveButton(R.string.close, null)
|
||||
.setOnDismissListener {
|
||||
onDismissListener?.invoke(TAG)
|
||||
}
|
||||
.show()
|
||||
val manager = app.gradesManager
|
||||
|
||||
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) 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)
|
||||
}
|
||||
if (historyList.isEmpty()) {
|
||||
b.historyVisible = false
|
||||
return@launch
|
||||
}
|
||||
b.historyVisible = true
|
||||
//b.gradeHistoryNest.isNestedScrollingEnabled = false
|
||||
b.gradeHistoryList.adapter = GradesAdapter(activity, {
|
||||
GradeDetailsDialog(activity, it)
|
||||
}).also { it.items = historyList.toMutableList() }
|
||||
b.gradeHistoryList.apply {
|
||||
setHasFixedSize(true)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
addItemDecoration(SimpleDividerItemDecoration(context))
|
||||
}
|
||||
}
|
||||
}}
|
||||
}
|
@ -6,14 +6,20 @@ package pl.szczodrzynski.edziennik.ui.dialogs.settings
|
||||
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.isVisible
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.config.ConfigGrades
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import it.sephiroth.android.library.numberpicker.doOnStopTrackingTouch
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.databinding.DialogConfigGradesBinding
|
||||
import pl.szczodrzynski.edziennik.setOnSelectedListener
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_DEFAULT
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_WEIGHTED
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.ORDER_BY_DATE_DESC
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.ORDER_BY_SUBJECT_ASC
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_AVG_2_AVG
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_AVG_2_SEM
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_SEM_2_AVG
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_SEM_2_SEM
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES
|
||||
|
||||
class GradesConfigDialog(
|
||||
val activity: AppCompatActivity,
|
||||
@ -42,6 +48,7 @@ class GradesConfigDialog(
|
||||
.setView(b.root)
|
||||
.setPositiveButton(R.string.ok) { dialog, _ -> dialog.dismiss() }
|
||||
.setOnDismissListener {
|
||||
saveConfig()
|
||||
onDismissListener?.invoke(TAG)
|
||||
if (reloadOnDismiss) (activity as? MainActivity)?.reloadTarget()
|
||||
}
|
||||
@ -52,43 +59,84 @@ class GradesConfigDialog(
|
||||
}}
|
||||
|
||||
private fun loadConfig() {
|
||||
b.customPlusCheckBox.isChecked = profileConfig.plusValue != null
|
||||
b.customPlusValue.isVisible = b.customPlusCheckBox.isChecked
|
||||
b.customMinusCheckBox.isChecked = profileConfig.minusValue != null
|
||||
b.customMinusValue.isVisible = b.customMinusCheckBox.isChecked
|
||||
|
||||
b.customPlusValue.progress = profileConfig.plusValue ?: 0.5f
|
||||
b.customMinusValue.progress = profileConfig.minusValue ?: 0.25f
|
||||
|
||||
when (config.orderBy) {
|
||||
ConfigGrades.ORDER_BY_DATE_DESC -> b.sortGradesByDateRadio
|
||||
ConfigGrades.ORDER_BY_SUBJECT_ASC -> b.sortGradesBySubjectRadio
|
||||
ORDER_BY_DATE_DESC -> b.sortGradesByDateRadio
|
||||
ORDER_BY_SUBJECT_ASC -> b.sortGradesBySubjectRadio
|
||||
else -> null
|
||||
}?.isChecked = true
|
||||
|
||||
when (profileConfig.colorMode) {
|
||||
Profile.COLOR_MODE_DEFAULT -> b.gradeColorFromERegister
|
||||
Profile.COLOR_MODE_WEIGHTED -> b.gradeColorByValue
|
||||
COLOR_MODE_DEFAULT -> b.gradeColorFromERegister
|
||||
COLOR_MODE_WEIGHTED -> b.gradeColorByValue
|
||||
else -> null
|
||||
}?.isChecked = true
|
||||
|
||||
when (profileConfig.yearAverageMode) {
|
||||
Profile.YEAR_ALL_GRADES -> b.gradeAverageMode4
|
||||
Profile.YEAR_1_AVG_2_AVG -> b.gradeAverageMode0
|
||||
Profile.YEAR_1_SEM_2_AVG -> b.gradeAverageMode1
|
||||
Profile.YEAR_1_AVG_2_SEM -> b.gradeAverageMode2
|
||||
Profile.YEAR_1_SEM_2_SEM -> b.gradeAverageMode3
|
||||
YEAR_ALL_GRADES -> b.gradeAverageMode4
|
||||
YEAR_1_AVG_2_AVG -> b.gradeAverageMode0
|
||||
YEAR_1_SEM_2_AVG -> b.gradeAverageMode1
|
||||
YEAR_1_AVG_2_SEM -> b.gradeAverageMode2
|
||||
YEAR_1_SEM_2_SEM -> b.gradeAverageMode3
|
||||
else -> null
|
||||
}?.isChecked = true
|
||||
|
||||
b.dontCountZeroToAverage.isChecked = !profileConfig.countZeroToAvg
|
||||
b.hideImproved.isChecked = profileConfig.hideImproved
|
||||
b.averageWithoutWeight.isChecked = profileConfig.averageWithoutWeight
|
||||
}
|
||||
|
||||
private fun saveConfig() {
|
||||
profileConfig.plusValue = if (b.customPlusCheckBox.isChecked) b.customPlusValue.progress else null
|
||||
profileConfig.minusValue = if (b.customMinusCheckBox.isChecked) b.customMinusValue.progress else null
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
b.sortGradesByDateRadio.setOnSelectedListener { config.orderBy = ConfigGrades.ORDER_BY_DATE_DESC }
|
||||
b.sortGradesBySubjectRadio.setOnSelectedListener { config.orderBy = ConfigGrades.ORDER_BY_SUBJECT_ASC }
|
||||
b.customPlusCheckBox.onChange { _, isChecked ->
|
||||
b.customPlusValue.isVisible = isChecked
|
||||
}
|
||||
b.customMinusCheckBox.onChange { _, isChecked ->
|
||||
b.customMinusValue.isVisible = isChecked
|
||||
}
|
||||
|
||||
b.gradeColorFromERegister.setOnSelectedListener { profileConfig.colorMode = Profile.COLOR_MODE_DEFAULT }
|
||||
b.gradeColorByValue.setOnSelectedListener { profileConfig.colorMode = Profile.COLOR_MODE_WEIGHTED }
|
||||
// who the hell named those methods
|
||||
// THIS SHIT DOES NOT EVEN WORK
|
||||
b.customPlusValue.doOnStopTrackingTouch {
|
||||
profileConfig.plusValue = it.progress
|
||||
}
|
||||
b.customMinusValue.doOnStopTrackingTouch {
|
||||
profileConfig.minusValue = it.progress
|
||||
}
|
||||
|
||||
b.gradeAverageMode4.setOnSelectedListener { profileConfig.yearAverageMode = Profile.YEAR_ALL_GRADES }
|
||||
b.gradeAverageMode0.setOnSelectedListener { profileConfig.yearAverageMode = Profile.YEAR_1_AVG_2_AVG }
|
||||
b.gradeAverageMode1.setOnSelectedListener { profileConfig.yearAverageMode = Profile.YEAR_1_SEM_2_AVG }
|
||||
b.gradeAverageMode2.setOnSelectedListener { profileConfig.yearAverageMode = Profile.YEAR_1_AVG_2_SEM }
|
||||
b.gradeAverageMode3.setOnSelectedListener { profileConfig.yearAverageMode = Profile.YEAR_1_SEM_2_SEM }
|
||||
b.sortGradesByDateRadio.setOnSelectedListener { config.orderBy = ORDER_BY_DATE_DESC }
|
||||
b.sortGradesBySubjectRadio.setOnSelectedListener { config.orderBy = ORDER_BY_SUBJECT_ASC }
|
||||
|
||||
b.dontCountZeroToAverage.setOnCheckedChangeListener { _, isChecked -> profileConfig.countZeroToAvg = !isChecked }
|
||||
b.gradeColorFromERegister.setOnSelectedListener { profileConfig.colorMode = COLOR_MODE_DEFAULT }
|
||||
b.gradeColorByValue.setOnSelectedListener { profileConfig.colorMode = COLOR_MODE_WEIGHTED }
|
||||
|
||||
b.gradeAverageMode4.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_ALL_GRADES }
|
||||
b.gradeAverageMode0.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_1_AVG_2_AVG }
|
||||
b.gradeAverageMode1.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_1_SEM_2_AVG }
|
||||
b.gradeAverageMode2.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_1_AVG_2_SEM }
|
||||
b.gradeAverageMode3.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_1_SEM_2_SEM }
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,10 +75,6 @@ class ProfileRemoveDialog(
|
||||
app.db.teamDao().clear(profileId)
|
||||
app.db.timetableDao().clear(profileId)
|
||||
|
||||
val homeCards = app.config.ui.homeCards.toMutableList()
|
||||
homeCards.removeAll { it.profileId == profileId }
|
||||
app.config.ui.homeCards = homeCards
|
||||
|
||||
val loginStoreId = profileObject.loginStoreId
|
||||
val profilesUsingLoginStore = app.db.profileDao().getIdsByLoginStoreIdNow(loginStoreId)
|
||||
if (profilesUsingLoginStore.size == 1) {
|
||||
|
@ -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()
|
||||
@ -77,36 +88,31 @@ class GenerateBlockTimetableDialog(
|
||||
|
||||
b = DialogGenerateBlockTimetableBinding.inflate(activity.layoutInflater)
|
||||
|
||||
b.showProfileNameItem.onClick { b.showProfileNameCheckbox.trigger() }
|
||||
b.withChangesCurrentWeekRadio.setText(R.string.timetable_generate_current_week_format, weekCurrentStart.formattedStringShort, weekCurrentEnd.formattedStringShort)
|
||||
b.withChangesNextWeekRadio.setText(R.string.timetable_generate_next_week_format, weekNextStart.formattedStringShort, weekCurrentEnd.formattedStringShort)
|
||||
|
||||
b.showProfileNameCheckbox.setOnCheckedChangeListener { _, isChecked -> showProfileName = isChecked }
|
||||
|
||||
b.showTeachersNamesItem.onClick { b.showTeachersNamesCheckbox.trigger() }
|
||||
b.showTeachersNamesCheckbox.setOnCheckedChangeListener { _, isChecked -> showTeachersNames = isChecked }
|
||||
|
||||
b.noColorsItem.onClick { b.noColorsCheckbox.trigger() }
|
||||
b.noColorsCheckbox.setOnCheckedChangeListener { _, isChecked -> noColors = isChecked }
|
||||
|
||||
dialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.timetable_generate_range)
|
||||
.setItems(arrayOf(
|
||||
activity.getString(R.string.timetable_generate_current_week_format, weekCurrentStart.formattedStringShort, weekCurrentEnd.formattedStringShort)
|
||||
.asColoredSpannable(android.R.attr.textColorPrimary.resolveAttr(activity)),
|
||||
activity.getString(R.string.timetable_generate_next_week_format, weekNextStart.formattedStringShort, weekNextEnd.formattedStringShort)
|
||||
.asColoredSpannable(android.R.attr.textColorPrimary.resolveAttr(activity)),
|
||||
activity.getString(R.string.timetable_generate_selected_week)
|
||||
.asColoredSpannable(android.R.attr.textColorPrimary.resolveAttr(activity))
|
||||
)) { dialog, which ->
|
||||
dialog.dismiss()
|
||||
when (which) {
|
||||
0 -> generateBlockTimetable(weekCurrentStart, weekCurrentEnd)
|
||||
1 -> generateBlockTimetable(weekNextStart, weekNextEnd)
|
||||
2 -> selectDate()
|
||||
}
|
||||
}
|
||||
.setView(b.root)
|
||||
.setNeutralButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
|
||||
.setOnDismissListener { onDismissListener?.invoke(TAG) }
|
||||
.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()
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
private fun selectDate() {
|
||||
@ -124,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())
|
||||
@ -166,174 +186,212 @@ 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
|
||||
}
|
||||
|
||||
val diff = Time.diff(maxTime, minTime)
|
||||
dialog.dismiss()
|
||||
|
||||
val imageWidth = WIDTH_CONSTANT + maxWeekDay * (WIDTH_WEEKDAY + WIDTH_SPACING) - WIDTH_SPACING
|
||||
val imageHeight = heightProfileName + HEIGHT_CONSTANT + diff.inMinutes * HEIGHT_MINUTE + HEIGHT_FOOTER
|
||||
val bitmap = Bitmap.createBitmap(imageWidth + 20, imageHeight + 30, Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(bitmap)
|
||||
val uri = withContext(Dispatchers.Default) {
|
||||
|
||||
if (noColors) canvas.drawARGB(255, 255, 255, 255)
|
||||
else canvas.drawARGB(255, 225, 225, 225)
|
||||
val diff = Time.diff(maxTime, minTime)
|
||||
|
||||
val paint = Paint().apply {
|
||||
isAntiAlias = true
|
||||
isFilterBitmap = true
|
||||
isDither = true
|
||||
}
|
||||
val imageWidth = WIDTH_CONSTANT + maxWeekDay * (WIDTH_WEEKDAY + WIDTH_SPACING) - WIDTH_SPACING
|
||||
val imageHeight = heightProfileName + HEIGHT_CONSTANT + diff.inMinutes * HEIGHT_MINUTE + HEIGHT_FOOTER
|
||||
val bitmap = Bitmap.createBitmap(imageWidth + 20, imageHeight + 30, Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(bitmap)
|
||||
|
||||
lessons.forEach { lesson ->
|
||||
val lessonLength = Time.diff(lesson.displayEndTime, lesson.displayStartTime)
|
||||
val firstOffset = Time.diff(lesson.displayStartTime, minTime)
|
||||
val lessonWeekDay = lesson.displayDate!!.weekDay
|
||||
if (noColors) canvas.drawARGB(255, 255, 255, 255)
|
||||
else canvas.drawARGB(255, 225, 225, 225)
|
||||
|
||||
val left = WIDTH_CONSTANT + lessonWeekDay * (WIDTH_WEEKDAY + WIDTH_SPACING)
|
||||
val top = heightProfileName + HEIGHT_CONSTANT + firstOffset.inMinutes * HEIGHT_MINUTE
|
||||
|
||||
val blockWidth = WIDTH_WEEKDAY
|
||||
val blockHeight = lessonLength.inMinutes * HEIGHT_MINUTE
|
||||
|
||||
val viewWidth = 380.dp
|
||||
val viewHeight = lessonLength.inMinutes * 4.dp
|
||||
|
||||
val layout = activity.layoutInflater.inflate(R.layout.row_timetable_block_item, null) as LinearLayout
|
||||
|
||||
val item: LinearLayout = layout.findViewById(R.id.timetableItemLayout)
|
||||
val card: CardView = layout.findViewById(R.id.timetableItemCard)
|
||||
val subjectName: TextView = layout.findViewById(R.id.timetableItemSubjectName)
|
||||
val classroomName: TextView = layout.findViewById(R.id.timetableItemClassroomName)
|
||||
val teacherName: TextView = layout.findViewById(R.id.timetableItemTeacherName)
|
||||
val teamName: TextView = layout.findViewById(R.id.timetableItemTeamName)
|
||||
|
||||
if (noColors) {
|
||||
card.setCardBackgroundColor(Color.WHITE)
|
||||
card.cardElevation = 0f
|
||||
item.setBackgroundResource(R.drawable.bg_rounded_16dp_outline)
|
||||
subjectName.setTextColor(Color.BLACK)
|
||||
classroomName.setTextColor(0xffaaaaaa.toInt())
|
||||
teacherName.setTextColor(0xffaaaaaa.toInt())
|
||||
teamName.setTextColor(0xffaaaaaa.toInt())
|
||||
val paint = Paint().apply {
|
||||
isAntiAlias = true
|
||||
isFilterBitmap = true
|
||||
isDither = true
|
||||
}
|
||||
|
||||
subjectName.text = lesson.displaySubjectName ?: ""
|
||||
classroomName.text = lesson.displayClassroom ?: ""
|
||||
teacherName.text = lesson.displayTeacherName ?: ""
|
||||
teamName.text = lesson.displayTeamName ?: ""
|
||||
lessons.forEach { lesson ->
|
||||
val lessonLength = Time.diff(lesson.displayEndTime, lesson.displayStartTime)
|
||||
val firstOffset = Time.diff(lesson.displayStartTime, minTime)
|
||||
val lessonWeekDay = lesson.displayDate!!.weekDay
|
||||
|
||||
if (!showTeachersNames) teacherName.visibility = View.GONE
|
||||
val left = WIDTH_CONSTANT + lessonWeekDay * (WIDTH_WEEKDAY + WIDTH_SPACING)
|
||||
val top = heightProfileName + HEIGHT_CONSTANT + firstOffset.inMinutes * HEIGHT_MINUTE
|
||||
|
||||
when (lesson.type) {
|
||||
Lesson.TYPE_NORMAL -> {}
|
||||
Lesson.TYPE_CANCELLED, Lesson.TYPE_SHIFTED_SOURCE -> {
|
||||
card.setCardBackgroundColor(Color.BLACK)
|
||||
subjectName.setTextColor(Color.WHITE)
|
||||
subjectName.text = lesson.displaySubjectName?.asStrikethroughSpannable() ?: ""
|
||||
val blockWidth = WIDTH_WEEKDAY
|
||||
val blockHeight = lessonLength.inMinutes * HEIGHT_MINUTE
|
||||
|
||||
val viewWidth = 380.dp
|
||||
val viewHeight = lessonLength.inMinutes * 4.dp
|
||||
|
||||
val layout = activity.layoutInflater.inflate(R.layout.row_timetable_block_item, null) as LinearLayout
|
||||
|
||||
val item: LinearLayout = layout.findViewById(R.id.timetableItemLayout)
|
||||
val card: CardView = layout.findViewById(R.id.timetableItemCard)
|
||||
val subjectName: TextView = layout.findViewById(R.id.timetableItemSubjectName)
|
||||
val classroomName: TextView = layout.findViewById(R.id.timetableItemClassroomName)
|
||||
val teacherName: TextView = layout.findViewById(R.id.timetableItemTeacherName)
|
||||
val teamName: TextView = layout.findViewById(R.id.timetableItemTeamName)
|
||||
|
||||
if (noColors) {
|
||||
card.setCardBackgroundColor(Color.WHITE)
|
||||
card.cardElevation = 0f
|
||||
item.setBackgroundResource(R.drawable.bg_rounded_16dp_outline)
|
||||
subjectName.setTextColor(Color.BLACK)
|
||||
classroomName.setTextColor(0xffaaaaaa.toInt())
|
||||
teacherName.setTextColor(0xffaaaaaa.toInt())
|
||||
teamName.setTextColor(0xffaaaaaa.toInt())
|
||||
}
|
||||
else -> {
|
||||
card.setCardBackgroundColor(0xff234158.toInt())
|
||||
subjectName.setTextColor(Color.WHITE)
|
||||
subjectName.setTypeface(null, Typeface.BOLD_ITALIC)
|
||||
|
||||
subjectName.text = lesson.displaySubjectName ?: ""
|
||||
classroomName.text = lesson.displayClassroom ?: ""
|
||||
teacherName.text = lesson.displayTeacherName ?: ""
|
||||
teamName.text = lesson.displayTeamName ?: ""
|
||||
|
||||
if (!showTeachersNames) teacherName.visibility = View.GONE
|
||||
|
||||
when (lesson.type) {
|
||||
Lesson.TYPE_NORMAL -> {
|
||||
}
|
||||
Lesson.TYPE_CANCELLED, Lesson.TYPE_SHIFTED_SOURCE -> {
|
||||
card.setCardBackgroundColor(Color.BLACK)
|
||||
subjectName.setTextColor(Color.WHITE)
|
||||
subjectName.text = lesson.displaySubjectName?.asStrikethroughSpannable()
|
||||
?: ""
|
||||
}
|
||||
else -> {
|
||||
card.setCardBackgroundColor(0xff234158.toInt())
|
||||
subjectName.setTextColor(Color.WHITE)
|
||||
subjectName.setTypeface(null, Typeface.BOLD_ITALIC)
|
||||
}
|
||||
}
|
||||
|
||||
layout.isDrawingCacheEnabled = true
|
||||
layout.measure(MeasureSpec.makeMeasureSpec(viewWidth, MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.EXACTLY))
|
||||
layout.layout(0, 0, layout.measuredWidth, layout.measuredHeight)
|
||||
layout.buildDrawingCache(true)
|
||||
|
||||
val itemBitmap = layout.drawingCache
|
||||
canvas.drawBitmap(itemBitmap, null, Rect(left, top, left + blockWidth, top + blockHeight), paint)
|
||||
}
|
||||
|
||||
val textPaint = Paint().apply {
|
||||
setARGB(255, 0, 0, 0)
|
||||
textAlign = Paint.Align.CENTER
|
||||
textSize = 30f
|
||||
isAntiAlias = true
|
||||
isFilterBitmap = true
|
||||
isDither = true
|
||||
}
|
||||
|
||||
for (w in 0..maxWeekDay) {
|
||||
val x = WIDTH_CONSTANT + w * WIDTH_WEEKDAY + w * WIDTH_SPACING
|
||||
canvas.drawText(Week.getFullDayName(w), x + (WIDTH_WEEKDAY / 2f), heightProfileName + HEIGHT_CONSTANT / 2 + 10f, textPaint)
|
||||
}
|
||||
|
||||
if (showProfileName) {
|
||||
textPaint.textSize = 50f
|
||||
canvas.drawText("${app.profile.name} - plan lekcji, ${weekStart.formattedStringShort} - ${weekEnd.formattedStringShort}", (imageWidth + 20) / 2f, 80f, textPaint)
|
||||
}
|
||||
|
||||
textPaint.apply {
|
||||
setARGB(128, 0, 0, 0)
|
||||
textAlign = Paint.Align.RIGHT
|
||||
textSize = 26f
|
||||
typeface = Typeface.create(Typeface.DEFAULT, Typeface.ITALIC)
|
||||
}
|
||||
|
||||
val footerTextPaintCenter = ((textPaint.descent() + textPaint.ascent()) / 2).roundToInt()
|
||||
canvas.drawText("Wygenerowano w aplikacji Szkolny.eu", imageWidth - 10f, imageHeight - footerTextPaintCenter - 10f, textPaint)
|
||||
|
||||
textPaint.apply {
|
||||
setARGB(255, 127, 127, 127)
|
||||
textAlign = Paint.Align.CENTER
|
||||
textSize = 16f
|
||||
typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
|
||||
}
|
||||
|
||||
val textPaintCenter = ((textPaint.descent() + textPaint.ascent()) / 2).roundToInt()
|
||||
|
||||
val linePaint = Paint().apply {
|
||||
setARGB(255, 100, 100, 100)
|
||||
style = Paint.Style.STROKE
|
||||
pathEffect = DashPathEffect(floatArrayOf(10f, 10f), 0f)
|
||||
isAntiAlias = true
|
||||
isFilterBitmap = true
|
||||
isDither = true
|
||||
}
|
||||
|
||||
val minTimeInt = ((minTime!!.value / 10000) * 60) + ((minTime!!.value / 100) % 100)
|
||||
|
||||
lessonRanges.forEach { (startTime, endTime) ->
|
||||
listOf(startTime, endTime).forEach { value ->
|
||||
val hour = value / 10000
|
||||
val minute = (value / 100) % 100
|
||||
val time = Time(hour, minute, 0)
|
||||
|
||||
val firstOffset = time.inMinutes - minTimeInt // offset in minutes
|
||||
val top = (heightProfileName + HEIGHT_CONSTANT + firstOffset * HEIGHT_MINUTE).toFloat()
|
||||
|
||||
canvas.drawText(time.stringHM, WIDTH_CONSTANT / 2f, top - textPaintCenter, textPaint)
|
||||
canvas.drawLine(WIDTH_CONSTANT.toFloat(), top, imageWidth.toFloat(), top, linePaint)
|
||||
}
|
||||
}
|
||||
|
||||
layout.isDrawingCacheEnabled = true
|
||||
layout.measure(MeasureSpec.makeMeasureSpec(viewWidth, MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.EXACTLY))
|
||||
layout.layout(0, 0, layout.measuredWidth, layout.measuredHeight)
|
||||
layout.buildDrawingCache(true)
|
||||
val today = Date.getToday().stringY_m_d
|
||||
val now = Time.getNow().stringH_M_S
|
||||
|
||||
val itemBitmap = layout.drawingCache
|
||||
canvas.drawBitmap(itemBitmap, null, Rect(left, top, left + blockWidth, top + blockHeight), paint)
|
||||
}
|
||||
val outputDir = Environment.getExternalStoragePublicDirectory("Szkolny.eu").apply { mkdirs() }
|
||||
val outputFile = File(outputDir, "plan_lekcji_${app.profile.name}_${today}_${now}.png")
|
||||
|
||||
val textPaint = Paint().apply {
|
||||
setARGB(255, 0, 0, 0)
|
||||
textAlign = Paint.Align.CENTER
|
||||
textSize = 30f
|
||||
isAntiAlias = true
|
||||
isFilterBitmap = true
|
||||
isDither = true
|
||||
}
|
||||
|
||||
for (w in 0..maxWeekDay) {
|
||||
val x = WIDTH_CONSTANT + w * WIDTH_WEEKDAY + w * WIDTH_SPACING
|
||||
canvas.drawText(Week.getFullDayName(w), x + (WIDTH_WEEKDAY / 2f), heightProfileName + HEIGHT_CONSTANT / 2 + 10f, textPaint)
|
||||
}
|
||||
|
||||
if (showProfileName) {
|
||||
textPaint.textSize = 50f
|
||||
canvas.drawText("${app.profile.name} - plan lekcji, ${weekStart.formattedStringShort} - ${weekEnd.formattedStringShort}", (imageWidth + 20) / 2f, 80f, textPaint)
|
||||
}
|
||||
|
||||
textPaint.apply {
|
||||
setARGB(128, 0, 0, 0)
|
||||
textAlign = Paint.Align.RIGHT
|
||||
textSize = 26f
|
||||
typeface = Typeface.create(Typeface.DEFAULT, Typeface.ITALIC)
|
||||
}
|
||||
|
||||
val footerTextPaintCenter = ((textPaint.descent() + textPaint.ascent()) / 2).roundToInt()
|
||||
canvas.drawText("Wygenerowano w aplikacji Szkolny.eu", imageWidth - 10f, imageHeight - footerTextPaintCenter - 10f, textPaint)
|
||||
|
||||
textPaint.apply {
|
||||
setARGB(255, 127, 127, 127)
|
||||
textAlign = Paint.Align.CENTER
|
||||
textSize = 16f
|
||||
typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
|
||||
}
|
||||
|
||||
val textPaintCenter = ((textPaint.descent() + textPaint.ascent()) / 2).roundToInt()
|
||||
|
||||
val linePaint = Paint().apply {
|
||||
setARGB(255, 100, 100, 100)
|
||||
style = Paint.Style.STROKE
|
||||
pathEffect = DashPathEffect(floatArrayOf(10f, 10f), 0f)
|
||||
isAntiAlias = true
|
||||
isFilterBitmap = true
|
||||
isDither = true
|
||||
}
|
||||
|
||||
val minTimeInt = ((minTime!!.value / 10000) * 60) + ((minTime!!.value / 100) % 100)
|
||||
|
||||
lessonRanges.forEach { (startTime, endTime) ->
|
||||
listOf(startTime, endTime).forEach { value ->
|
||||
val hour = value / 10000
|
||||
val minute = (value / 100) % 100
|
||||
val time = Time(hour, minute, 0)
|
||||
|
||||
val firstOffset = time.inMinutes - minTimeInt // offset in minutes
|
||||
val top = (heightProfileName + HEIGHT_CONSTANT + firstOffset * HEIGHT_MINUTE).toFloat()
|
||||
|
||||
canvas.drawText(time.stringHM, WIDTH_CONSTANT / 2f, top - textPaintCenter, textPaint)
|
||||
canvas.drawLine(WIDTH_CONSTANT.toFloat(), top, imageWidth.toFloat(), top, linePaint)
|
||||
try {
|
||||
val fos = FileOutputStream(outputFile)
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)
|
||||
fos.close()
|
||||
} catch (e: Exception) {
|
||||
Log.e("SAVE_IMAGE", e.message, e)
|
||||
return@withContext null
|
||||
}
|
||||
}
|
||||
|
||||
val today = Date.getToday().stringY_m_d
|
||||
val now = Time.getNow().stringH_M_S
|
||||
|
||||
val outputDir = Environment.getExternalStoragePublicDirectory("Szkolny.eu").apply { mkdirs() }
|
||||
val outputFile = File(outputDir, "plan_lekcji_${app.profile.name}_${today}_${now}.png")
|
||||
|
||||
try {
|
||||
val fos = FileOutputStream(outputFile)
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)
|
||||
fos.close()
|
||||
} catch (e: Exception) {
|
||||
Log.e("SAVE_IMAGE", e.message, e)
|
||||
return@launch
|
||||
}
|
||||
|
||||
val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
FileProvider.getUriForFile(activity, app.packageName + ".provider", outputFile)
|
||||
} else {
|
||||
Uri.parse("file://" + outputFile.absolutePath)
|
||||
val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
FileProvider.getUriForFile(activity, app.packageName + ".provider", outputFile)
|
||||
} else {
|
||||
Uri.parse("file://" + outputFile.absolutePath)
|
||||
}
|
||||
uri
|
||||
}
|
||||
|
||||
progressDialog.dismiss()
|
||||
|
@ -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>()
|
||||
|
@ -36,6 +36,7 @@ import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Subject;
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull;
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentAttendanceBinding;
|
||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration;
|
||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem;
|
||||
|
||||
@ -178,6 +179,7 @@ public class AttendanceFragment extends Fragment {
|
||||
|
||||
b.attendanceView.setHasFixedSize(true);
|
||||
b.attendanceView.setLayoutManager(linearLayoutManager);
|
||||
b.attendanceView.addItemDecoration(new SimpleDividerItemDecoration(getContext()));
|
||||
|
||||
App.db.attendanceDao().getAll(App.Companion.getProfileId()).observe(this, attendance -> {
|
||||
if (app == null || activity == null || b == null || !isAdded())
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
@ -10,9 +10,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentTemplateBinding
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class TemplateFragment : Fragment(), CoroutineScope {
|
||||
@ -34,18 +32,13 @@ class TemplateFragment : Fragment(), CoroutineScope {
|
||||
activity = (getActivity() as MainActivity?) ?: return null
|
||||
context ?: return null
|
||||
app = activity.application as App
|
||||
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 = FragmentTemplateBinding.inflate(inflater)
|
||||
b.refreshLayout.setParent(activity.swipeRefreshLayout)
|
||||
return b.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
// TODO check if app, activity, b can be null
|
||||
if (app.profile == null || !isAdded)
|
||||
if (!isAdded)
|
||||
return
|
||||
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-3-1.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.grades
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.text.TextUtils
|
||||
import android.util.AttributeSet
|
||||
import android.util.TypedValue.COMPLEX_UNIT_SP
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
import android.widget.LinearLayout
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||
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.dp
|
||||
import pl.szczodrzynski.edziennik.resolveAttr
|
||||
import pl.szczodrzynski.edziennik.setTintColor
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager
|
||||
|
||||
class GradeView : AppCompatTextView {
|
||||
|
||||
@JvmOverloads
|
||||
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : super(context, attrs, defStyleAttr)
|
||||
|
||||
constructor(context: Context, grade: Grade, manager: GradesManager, periodGradesTextual: Boolean = false) : this(context, null) {
|
||||
setGrade(grade, manager, false, periodGradesTextual)
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
fun setGrade(grade: Grade?, manager: GradesManager, bigView: Boolean = false, periodGradesTextual: Boolean = false) {
|
||||
if (grade == null) {
|
||||
visibility = View.GONE
|
||||
return
|
||||
}
|
||||
visibility = View.VISIBLE
|
||||
|
||||
val gradeName = grade.name
|
||||
|
||||
val gradeColor = manager.getGradeColor(grade)
|
||||
|
||||
text = if (periodGradesTextual)
|
||||
when (grade.type) {
|
||||
TYPE_SEMESTER1_PROPOSED, TYPE_SEMESTER2_PROPOSED -> context.getString(
|
||||
R.string.grade_semester_proposed_format,
|
||||
gradeName
|
||||
)
|
||||
TYPE_SEMESTER1_FINAL, TYPE_SEMESTER2_FINAL -> context.getString(
|
||||
R.string.grade_semester_final_format,
|
||||
gradeName
|
||||
)
|
||||
TYPE_YEAR_PROPOSED -> context.getString(
|
||||
R.string.grade_year_proposed_format,
|
||||
gradeName
|
||||
)
|
||||
TYPE_YEAR_FINAL -> context.getString(
|
||||
R.string.grade_year_final_format,
|
||||
gradeName
|
||||
)
|
||||
else -> gradeName
|
||||
}
|
||||
else
|
||||
gradeName
|
||||
|
||||
setTextColor(when (grade.type) {
|
||||
TYPE_SEMESTER1_PROPOSED,
|
||||
TYPE_SEMESTER2_PROPOSED,
|
||||
TYPE_YEAR_PROPOSED -> android.R.attr.textColorPrimary.resolveAttr(context)
|
||||
else -> if (ColorUtils.calculateLuminance(gradeColor) > 0.3)
|
||||
0x99000000.toInt()
|
||||
else
|
||||
0x99ffffff.toInt()
|
||||
})
|
||||
|
||||
//typeface = Typeface.create("sans-serif-light", Typeface.NORMAL)
|
||||
setBackgroundResource(when (grade.type) {
|
||||
TYPE_SEMESTER1_PROPOSED,
|
||||
TYPE_SEMESTER2_PROPOSED,
|
||||
TYPE_YEAR_PROPOSED -> if (bigView) R.drawable.bg_rounded_8dp_outline else R.drawable.bg_rounded_4dp_outline
|
||||
else -> if (bigView) R.drawable.bg_rounded_8dp else R.drawable.bg_rounded_4dp
|
||||
})
|
||||
background.setTintColor(gradeColor)
|
||||
gravity = Gravity.CENTER
|
||||
|
||||
if (bigView) {
|
||||
setTextSize(COMPLEX_UNIT_SP, 24f)
|
||||
setAutoSizeTextTypeUniformWithConfiguration(
|
||||
14,
|
||||
32,
|
||||
1,
|
||||
COMPLEX_UNIT_SP
|
||||
)
|
||||
setPadding(2.dp, 2.dp, 2.dp, 2.dp)
|
||||
}
|
||||
else {
|
||||
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)
|
||||
}
|
||||
maxLines = 1
|
||||
ellipsize = TextUtils.TruncateAt.END
|
||||
measure(WRAP_CONTENT, WRAP_CONTENT)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-2-29.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.grades
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.view.LayoutInflater
|
||||
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>(), CoroutineScope {
|
||||
companion object {
|
||||
private const val TAG = "GradesAdapter"
|
||||
private const val ITEM_TYPE_SUBJECT = 0
|
||||
private const val ITEM_TYPE_SEMESTER = 1
|
||||
private const val ITEM_TYPE_EMPTY = 2
|
||||
private const val ITEM_TYPE_GRADE = 3
|
||||
private const val ITEM_TYPE_STATS = 4
|
||||
const val STATE_CLOSED = 0
|
||||
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 {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
return when (viewType) {
|
||||
ITEM_TYPE_SUBJECT -> SubjectViewHolder(inflater, parent)
|
||||
ITEM_TYPE_SEMESTER -> SemesterViewHolder(inflater, parent)
|
||||
ITEM_TYPE_EMPTY -> EmptyViewHolder(inflater, parent)
|
||||
ITEM_TYPE_GRADE -> GradeViewHolder(inflater, parent)
|
||||
ITEM_TYPE_STATS -> StatsViewHolder(inflater, parent)
|
||||
else -> throw IllegalArgumentException("Incorrect viewType")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return when (items[position]) {
|
||||
is GradesSubject -> ITEM_TYPE_SUBJECT
|
||||
is GradesSemester -> ITEM_TYPE_SEMESTER
|
||||
is GradesEmpty -> ITEM_TYPE_EMPTY
|
||||
is Grade -> ITEM_TYPE_GRADE
|
||||
is GradesStats -> ITEM_TYPE_STATS
|
||||
else -> throw IllegalArgumentException("Incorrect viewType")
|
||||
}
|
||||
}
|
||||
|
||||
private val onClickListener = View.OnClickListener { view ->
|
||||
val model = view.getTag(R.string.tag_key_model)
|
||||
if (model is GradeFull) {
|
||||
onGradeClick?.invoke(model)
|
||||
return@OnClickListener
|
||||
}
|
||||
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
|
||||
|
||||
if (model is GradesSubject || model is GradesSemester) {
|
||||
view?.findViewById<View>(R.id.dropdownIcon)?.let { dropdownIcon ->
|
||||
ObjectAnimator.ofFloat(
|
||||
dropdownIcon,
|
||||
View.ROTATION,
|
||||
if (model.state == STATE_CLOSED) 0f else 180f,
|
||||
if (model.state == STATE_CLOSED) 180f else 0f
|
||||
).setDuration(200).start();
|
||||
}
|
||||
}
|
||||
if (model is GradesSubject) {
|
||||
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 = when {
|
||||
model is GradesSemester && model.grades.isEmpty() ->
|
||||
listOf(GradesEmpty())
|
||||
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())
|
||||
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 = when {
|
||||
semester.grades.isEmpty() ->
|
||||
listOf(GradesEmpty())
|
||||
manager.hideImproved ->
|
||||
semester.grades.filter { !it.seen || !it.isImproved }
|
||||
else -> semester.grades
|
||||
}
|
||||
|
||||
semester.state = STATE_OPENED
|
||||
items.addAll(position + 2 + semesterIndex, grades)
|
||||
if (notifyAdapter) notifyItemRangeInserted(position + 2 + semesterIndex, grades.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
val start = position + 1
|
||||
var end: Int = items.size
|
||||
for (i in start until items.size) {
|
||||
val model1 = items[i]
|
||||
val level = if (model1 is GradesStats) 0 else (model1 as? ExpandableItemModel<*>)?.level ?: 3
|
||||
if (level <= model.level) {
|
||||
end = i
|
||||
break
|
||||
} else {
|
||||
if (model1 is ExpandableItemModel<*> && model1.state == STATE_OPENED) {
|
||||
model1.state = STATE_CLOSED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (end != -1) {
|
||||
items.subList(start, end).clear()
|
||||
if (notifyAdapter) notifyItemRangeRemoved(start, end - start)
|
||||
}
|
||||
|
||||
model.state = STATE_CLOSED
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
val item = items[position]
|
||||
if (holder !is BindableViewHolder<*>)
|
||||
return
|
||||
|
||||
val viewType = when (holder) {
|
||||
is SubjectViewHolder -> ITEM_TYPE_SUBJECT
|
||||
is SemesterViewHolder -> ITEM_TYPE_SEMESTER
|
||||
is EmptyViewHolder -> ITEM_TYPE_EMPTY
|
||||
is GradeViewHolder -> ITEM_TYPE_GRADE
|
||||
is StatsViewHolder -> ITEM_TYPE_STATS
|
||||
else -> throw IllegalArgumentException("Incorrect viewType")
|
||||
}
|
||||
holder.itemView.setTag(R.string.tag_key_view_type, viewType)
|
||||
holder.itemView.setTag(R.string.tag_key_position, position)
|
||||
holder.itemView.setTag(R.string.tag_key_model, item)
|
||||
|
||||
when {
|
||||
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) {
|
||||
holder.b.editButton.onClick {
|
||||
val subject = items.firstOrNull { it is GradesSubject && it.subjectId == item.subjectId } as? GradesSubject ?: return@onClick
|
||||
onGradesEditorClick?.invoke(subject, item)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
@ -1,513 +0,0 @@
|
||||
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.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.MainActivity;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.config.ProfileConfigGrades;
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade;
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Subject;
|
||||
import pl.szczodrzynski.edziennik.data.db.full.GradeFull;
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentGradesBinding;
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog;
|
||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
||||
import pl.szczodrzynski.edziennik.utils.models.ItemGradesSubjectModel;
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem;
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.config.ConfigGrades.ORDER_BY_DATE_ASC;
|
||||
import static pl.szczodrzynski.edziennik.config.ConfigGrades.ORDER_BY_DATE_DESC;
|
||||
import static pl.szczodrzynski.edziennik.config.ConfigGrades.ORDER_BY_SUBJECT_ASC;
|
||||
import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_GRADE;
|
||||
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.YEAR_1_AVG_2_AVG;
|
||||
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.YEAR_1_AVG_2_SEM;
|
||||
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.YEAR_1_SEM_2_AVG;
|
||||
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.YEAR_1_SEM_2_SEM;
|
||||
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.YEAR_ALL_GRADES;
|
||||
|
||||
public class GradesFragment extends Fragment {
|
||||
|
||||
private App app = null;
|
||||
private MainActivity activity = null;
|
||||
private FragmentGradesBinding b = null;
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
activity = (MainActivity) getActivity();
|
||||
if (getActivity() == null || getContext() == null)
|
||||
return null;
|
||||
app = (App) activity.getApplication();
|
||||
getContext().getTheme().applyStyle(Themes.INSTANCE.getAppTheme(), true);
|
||||
// activity, context and profile is valid
|
||||
b = DataBindingUtil.inflate(inflater, R.layout.fragment_grades, container, false);
|
||||
b.refreshLayout.setParent(activity.getSwipeRefreshLayout());
|
||||
b.refreshLayout.setNestedScrollingEnabled(true);
|
||||
return b.getRoot();
|
||||
}
|
||||
|
||||
ListView listView;
|
||||
List<ItemGradesSubjectModel> subjectList;
|
||||
|
||||
private String getRegisterCardAverageModeSubText() {
|
||||
switch (App.Companion.getConfig().forProfile().getGrades().getYearAverageMode()) {
|
||||
default:
|
||||
case YEAR_1_AVG_2_AVG:
|
||||
return getString(R.string.settings_register_avg_mode_0_short);
|
||||
case YEAR_1_SEM_2_AVG:
|
||||
return getString(R.string.settings_register_avg_mode_1_short);
|
||||
case YEAR_1_AVG_2_SEM:
|
||||
return getString(R.string.settings_register_avg_mode_2_short);
|
||||
case YEAR_1_SEM_2_SEM:
|
||||
return getString(R.string.settings_register_avg_mode_3_short);
|
||||
case YEAR_ALL_GRADES:
|
||||
return getString(R.string.settings_register_avg_mode_4_short);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
if (app == null || activity == null || b == null || !isAdded())
|
||||
return;
|
||||
|
||||
/*activity.getBottomSheet().setToggleGroupEnabled(true);
|
||||
activity.getBottomSheet().toggleGroupRemoveItems();
|
||||
activity.getBottomSheet().setToggleGroupSelectionMode(NavBottomSheet.TOGGLE_GROUP_SORTING_ORDER);
|
||||
activity.getBottomSheet().toggleGroupAddItem(0, getString(R.string.sort_by_date), (Drawable)null, SORT_MODE_DESCENDING);
|
||||
activity.getBottomSheet().toggleGroupAddItem(1, getString(R.string.sort_by_subject), (Drawable)null, SORT_MODE_ASCENDING);
|
||||
activity.getBottomSheet().setToggleGroupSortingOrderListener((id, sortMode) -> {
|
||||
sortModeChanged = true;
|
||||
if (id == 0 && sortMode == SORT_MODE_ASCENDING) {
|
||||
app.appConfig.gradesOrderBy = ORDER_BY_DATE_ASC;
|
||||
}
|
||||
else if (id == 1 && sortMode == SORT_MODE_ASCENDING) {
|
||||
app.appConfig.gradesOrderBy = ORDER_BY_SUBJECT_ASC;
|
||||
}
|
||||
else if (id == 0 && sortMode == SORT_MODE_DESCENDING) {
|
||||
app.appConfig.gradesOrderBy = ORDER_BY_DATE_DESC;
|
||||
}
|
||||
else if (id == 1 && sortMode == SORT_MODE_DESCENDING) {
|
||||
app.appConfig.gradesOrderBy = ORDER_BY_SUBJECT_DESC;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
activity.getBottomSheet().setToggleGroupTitle("Sortowanie");
|
||||
activity.getBottomSheet().toggleGroupCheck(0);
|
||||
activity.getBottomSheet().setOnCloseListener(() -> {
|
||||
if (sortModeChanged) {
|
||||
sortModeChanged = false;
|
||||
activity.reloadTarget();
|
||||
}
|
||||
return null;
|
||||
});*/
|
||||
|
||||
activity.getBottomSheet().prependItems(
|
||||
new BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_grades_averages)
|
||||
.withDescription(R.string.menu_grades_averages_desc)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_chart_line)
|
||||
.withOnClickListener(v3 -> {
|
||||
activity.getBottomSheet().close();
|
||||
showAverages();
|
||||
}),
|
||||
new BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_grades_config)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_settings_outline)
|
||||
.withOnClickListener(v3 -> {
|
||||
activity.getBottomSheet().close();
|
||||
new GradesConfigDialog(activity, true, null, null);
|
||||
}),
|
||||
new BottomSheetSeparatorItem(true),
|
||||
new BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_mark_as_read)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
|
||||
.withOnClickListener(v3 -> {
|
||||
activity.getBottomSheet().close();
|
||||
AsyncTask.execute(() -> App.db.metadataDao().setAllSeen(App.Companion.getProfileId(), TYPE_GRADE, true));
|
||||
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show();
|
||||
})
|
||||
);
|
||||
activity.gainAttention();
|
||||
|
||||
/*b.refreshLayout.setOnRefreshListener(() -> {
|
||||
activity.syncCurrentFeature(MainActivity.DRAWER_ITEM_GRADES, b.refreshLayout);
|
||||
});*/
|
||||
|
||||
listView = b.gradesRecyclerView;
|
||||
//listView.setHasFixedSize(true);
|
||||
//listView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
|
||||
long expandSubjectId = -1;
|
||||
if (getArguments() != null) {
|
||||
expandSubjectId = getArguments().getLong("gradesSubjectId", -1);
|
||||
}
|
||||
|
||||
/*b.gradesSwipeLayout.setOnRefreshListener(() -> {
|
||||
Toast.makeText(activity, "Works!", Toast.LENGTH_LONG).show();
|
||||
// To keep animation for 4 seconds
|
||||
new Handler().postDelayed(() -> {
|
||||
// Stop animation (This will be after 3 seconds)
|
||||
b.gradesSwipeLayout.setRefreshing(false);
|
||||
}, 3000);
|
||||
});*/
|
||||
|
||||
long finalExpandSubjectId = expandSubjectId;
|
||||
String orderBy;
|
||||
if (app.getConfig().getGrades().getOrderBy() == ORDER_BY_SUBJECT_ASC) {
|
||||
orderBy = "subjectLongName ASC, addedDate DESC";
|
||||
}
|
||||
else if (app.getConfig().getGrades().getOrderBy() == ORDER_BY_DATE_DESC) {
|
||||
orderBy = "addedDate DESC";
|
||||
}
|
||||
else if (app.getConfig().getGrades().getOrderBy() == ORDER_BY_DATE_ASC) {
|
||||
orderBy = "addedDate ASC";
|
||||
}
|
||||
else {
|
||||
orderBy = "subjectLongName DESC, addedDate DESC";
|
||||
}
|
||||
|
||||
App.db.gradeDao().getAllOrderBy(App.Companion.getProfileId(), orderBy).observe(this, grades -> {
|
||||
if (app == null || activity == null || b == null || !isAdded())
|
||||
return;
|
||||
|
||||
subjectList = new ArrayList<>();
|
||||
|
||||
ProfileConfigGrades config = app.getConfig().getFor(App.Companion.getProfileId()).getGrades();
|
||||
|
||||
// now we have all grades from the newest to the oldest
|
||||
for (GradeFull grade: grades) {
|
||||
ItemGradesSubjectModel model = ItemGradesSubjectModel.searchModelBySubjectId(subjectList, grade.subjectId);
|
||||
if (model == null) {
|
||||
model = new ItemGradesSubjectModel(app.getProfile(),
|
||||
new Subject(App.Companion.getProfileId(), grade.subjectId, grade.subjectLongName, grade.subjectShortName),
|
||||
new ArrayList<>(),
|
||||
new ArrayList<>());
|
||||
subjectList.add(model);
|
||||
if (model.subject != null && model.subject.id == finalExpandSubjectId) {
|
||||
model.expandView = true;
|
||||
}
|
||||
model.colorMode = App.Companion.getConfig().forProfile().getGrades().getColorMode();
|
||||
model.yearAverageMode = App.Companion.getConfig().forProfile().getGrades().getYearAverageMode();
|
||||
}
|
||||
if (!grade.seen && grade.semester == 1) {
|
||||
model.semester1Unread++;
|
||||
}
|
||||
if (!grade.seen && grade.semester == 2) {
|
||||
model.semester2Unread++;
|
||||
}
|
||||
// COUNT POINT GRADES
|
||||
if (grade.type == Grade.TYPE_POINT_AVG) {
|
||||
model.isPointSubject = true;
|
||||
if (grade.semester == 1) {
|
||||
model.gradeSumOverall += grade.value;
|
||||
model.gradeCountOverall += grade.valueMax;
|
||||
model.gradeSumSemester1 += grade.value;
|
||||
model.gradeCountSemester1 += grade.valueMax;
|
||||
model.semester1Average = model.gradeSumSemester1 / model.gradeCountSemester1 * 100;
|
||||
model.grades1.add(grade);
|
||||
}
|
||||
if (grade.semester == 2) {
|
||||
model.gradeSumOverall += grade.value;
|
||||
model.gradeCountOverall += grade.valueMax;
|
||||
model.gradeSumSemester2 += grade.value;
|
||||
model.gradeCountSemester2 += grade.valueMax;
|
||||
model.semester2Average = model.gradeSumSemester2 / model.gradeCountSemester2 * 100;
|
||||
model.grades2.add(grade);
|
||||
}
|
||||
}
|
||||
else if (grade.type == Grade.TYPE_POINT_SUM) {
|
||||
model.isBehaviourSubject = true;
|
||||
if (grade.semester == 1) {
|
||||
model.semester1Average += grade.value;
|
||||
model.yearAverage += grade.value;
|
||||
model.grades1.add(grade);
|
||||
}
|
||||
if (grade.semester == 2) {
|
||||
model.semester2Average += grade.value;
|
||||
model.yearAverage += grade.value;
|
||||
model.grades2.add(grade);
|
||||
}
|
||||
}
|
||||
else if (grade.type == Grade.TYPE_NORMAL) {
|
||||
model.isNormalSubject = true;
|
||||
float weight = grade.weight;
|
||||
if (weight < 0) {
|
||||
// do not show *normal* grades with negative weight - these are historical grades - Iuczniowie
|
||||
continue;
|
||||
}
|
||||
if (!config.getCountZeroToAvg() && grade.name.equals("0")) {
|
||||
weight = 0;
|
||||
}
|
||||
float valueWeighted = grade.value * weight;
|
||||
if (grade.semester == 1) {
|
||||
model.gradeSumOverall += valueWeighted;
|
||||
model.gradeCountOverall += weight;
|
||||
model.gradeSumSemester1 += valueWeighted;
|
||||
model.gradeCountSemester1 += weight;
|
||||
model.semester1Average = model.gradeSumSemester1 / model.gradeCountSemester1;
|
||||
if (grade.parentId == -1) {
|
||||
// show only "current" grades - these which are not historical
|
||||
model.grades1.add(grade);
|
||||
}
|
||||
}
|
||||
if (grade.semester == 2) {
|
||||
model.gradeSumOverall += valueWeighted;
|
||||
model.gradeCountOverall += weight;
|
||||
model.gradeSumSemester2 += valueWeighted;
|
||||
model.gradeCountSemester2 += weight;
|
||||
model.semester2Average = model.gradeSumSemester2 / model.gradeCountSemester2;
|
||||
if (grade.parentId == -1) {
|
||||
// show only "current" grades - these which are not historical
|
||||
model.grades2.add(grade);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (grade.type == Grade.TYPE_SEMESTER1_PROPOSED) {
|
||||
model.semester1Proposed = grade;
|
||||
}
|
||||
else if (grade.type == Grade.TYPE_SEMESTER1_FINAL) {
|
||||
model.semester1Final = grade;
|
||||
}
|
||||
else if (grade.type == Grade.TYPE_SEMESTER2_PROPOSED) {
|
||||
model.semester2Proposed = grade;
|
||||
}
|
||||
else if (grade.type == Grade.TYPE_SEMESTER2_FINAL) {
|
||||
model.semester2Final = grade;
|
||||
}
|
||||
else if (grade.type == Grade.TYPE_YEAR_PROPOSED) {
|
||||
model.yearProposed = grade;
|
||||
}
|
||||
else if (grade.type == Grade.TYPE_YEAR_FINAL) {
|
||||
model.yearFinal = grade;
|
||||
}
|
||||
else {
|
||||
// descriptive grades, text grades
|
||||
model.isDescriptiveSubject = true;
|
||||
if (grade.semester == 1) {
|
||||
model.grades1.add(grade);
|
||||
}
|
||||
if (grade.semester == 2) {
|
||||
model.grades2.add(grade);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (ItemGradesSubjectModel model: subjectList) {
|
||||
if (model.isPointSubject) {
|
||||
model.yearAverage = model.gradeSumOverall / model.gradeCountOverall * 100.0f; // map the point grade "average" % value from 0.0f-1.0f to 0%-100%
|
||||
}
|
||||
/*else if (model.isDescriptiveSubject && !model.isNormalSubject) {
|
||||
// applies for only descriptive grades - do nothing. average is hidden
|
||||
//model.isDescriptiveSubject = false;
|
||||
//model.semester1Average = -1;
|
||||
//model.semester2Average = -1;
|
||||
//model.yearAverage = -1;
|
||||
}*/
|
||||
else if (!model.isBehaviourSubject && model.isNormalSubject) {
|
||||
// applies for normal grades & normal+descriptive grades
|
||||
// calculate the normal grade average based on the user's setting
|
||||
switch (App.Companion.getConfig().forProfile().getGrades().getYearAverageMode()) {
|
||||
case YEAR_1_AVG_2_AVG:
|
||||
model.yearAverage = (model.semester1Average + model.semester2Average) / 2;
|
||||
break;
|
||||
case YEAR_1_SEM_2_AVG:
|
||||
model.yearAverage = model.semester1Final != null ? (model.semester1Final.value + model.semester2Average) / 2 : 0.0f;
|
||||
break;
|
||||
case YEAR_1_AVG_2_SEM:
|
||||
model.yearAverage = model.semester2Final != null ? (model.semester1Average + model.semester2Final.value) / 2 : 0.0f;
|
||||
break;
|
||||
case YEAR_1_SEM_2_SEM:
|
||||
model.yearAverage = model.semester1Final != null && model.semester2Final != null ? (model.semester1Final.value + model.semester2Final.value) / 2 : 0.0f;
|
||||
break;
|
||||
default:
|
||||
case YEAR_ALL_GRADES:
|
||||
model.yearAverage = model.gradeSumOverall / model.gradeCountOverall;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (subjectList.size() > 0) {
|
||||
GradesSubjectAdapter adapter;
|
||||
if ((adapter = (GradesSubjectAdapter) listView.getAdapter()) != null) {
|
||||
adapter.subjectList = subjectList;
|
||||
adapter.notifyDataSetChanged();
|
||||
return;
|
||||
}
|
||||
adapter = new GradesSubjectAdapter(subjectList, activity);
|
||||
listView.setAdapter(adapter);
|
||||
listView.setVisibility(View.VISIBLE);
|
||||
b.gradesNoData.setVisibility(View.GONE);
|
||||
if (finalExpandSubjectId != -1) {
|
||||
int subjectIndex = subjectList.indexOf(ItemGradesSubjectModel.searchModelBySubjectId(subjectList, finalExpandSubjectId));
|
||||
listView.setSelection(subjectIndex > 0 ? subjectIndex - 1 : subjectIndex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
listView.setVisibility(View.GONE);
|
||||
b.gradesNoData.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private int gradeFromAverage(float value) {
|
||||
int grade = (int)Math.floor(value);
|
||||
if (value % 1.0f >= 0.75f)
|
||||
grade++;
|
||||
return grade;
|
||||
}
|
||||
|
||||
public void showAverages() {
|
||||
if (app == null || activity == null || b == null || !isAdded() || subjectList == null)
|
||||
return;
|
||||
|
||||
float semester1Sum = 0;
|
||||
float semester1Count = 0;
|
||||
float semester1ProposedSum = 0;
|
||||
float semester1ProposedCount = 0;
|
||||
float semester1FinalSum = 0;
|
||||
float semester1FinalCount = 0;
|
||||
|
||||
float semester2Sum = 0;
|
||||
float semester2Count = 0;
|
||||
float semester2ProposedSum = 0;
|
||||
float semester2ProposedCount = 0;
|
||||
float semester2FinalSum = 0;
|
||||
float semester2FinalCount = 0;
|
||||
|
||||
float yearSum = 0;
|
||||
float yearCount = 0;
|
||||
float yearProposedSum = 0;
|
||||
float yearProposedCount = 0;
|
||||
float yearFinalSum = 0;
|
||||
float yearFinalCount = 0;
|
||||
|
||||
for (ItemGradesSubjectModel subject: subjectList) {
|
||||
// we cannot skip non-normal subjects because a point subject may also have a final grade
|
||||
if (subject.isBehaviourSubject)
|
||||
continue;
|
||||
|
||||
// SEMESTER 1 GRADES & AVERAGES
|
||||
if (subject.semester1Final != null && subject.semester1Final.value > 0) { // if final available, add to final grades & expected grades
|
||||
semester1FinalSum += subject.semester1Final.value;
|
||||
semester1FinalCount++;
|
||||
semester1Sum += subject.semester1Final.value;
|
||||
semester1Count++;
|
||||
}
|
||||
else if (subject.semester1Proposed != null && subject.semester1Proposed.value > 0) { // if final not available, add proposed to expected grades
|
||||
semester1Sum += subject.semester1Proposed.value;
|
||||
semester1Count++;
|
||||
}
|
||||
else if (!Float.isNaN(subject.semester1Average)
|
||||
&& subject.semester1Average > 0
|
||||
&& !subject.isPointSubject) { // if final&proposed unavailable, calculate from avg
|
||||
semester1Sum += gradeFromAverage(subject.semester1Average);
|
||||
semester1Count++;
|
||||
}
|
||||
if (subject.semester1Proposed != null && subject.semester1Proposed.value > 0) { // add proposed to proposed grades even if final is available
|
||||
semester1ProposedSum += subject.semester1Proposed.value;
|
||||
semester1ProposedCount++;
|
||||
}
|
||||
|
||||
// SEMESTER 2 GRADES & AVERAGES
|
||||
if (subject.semester2Final != null && subject.semester2Final.value > 0) { // if final available, add to final grades & expected grades
|
||||
semester2FinalSum += subject.semester2Final.value;
|
||||
semester2FinalCount++;
|
||||
semester2Sum += subject.semester2Final.value;
|
||||
semester2Count++;
|
||||
}
|
||||
else if (subject.semester2Proposed != null && subject.semester2Proposed.value > 0) { // if final not available, add proposed to expected grades
|
||||
semester2Sum += subject.semester2Proposed.value;
|
||||
semester2Count++;
|
||||
}
|
||||
else if (!Float.isNaN(subject.semester2Average)
|
||||
&& subject.semester2Average > 0
|
||||
&& !subject.isPointSubject) { // if final&proposed unavailable, calculate from avg
|
||||
semester2Sum += gradeFromAverage(subject.semester2Average);
|
||||
semester2Count++;
|
||||
}
|
||||
if (subject.semester2Proposed != null && subject.semester2Proposed.value > 0) { // add proposed to proposed grades even if final is available
|
||||
semester2ProposedSum += subject.semester2Proposed.value;
|
||||
semester2ProposedCount++;
|
||||
}
|
||||
|
||||
// YEAR GRADES & AVERAGES
|
||||
if (subject.yearFinal != null && subject.yearFinal.value > 0) { // if final available, add to final grades & expected grades
|
||||
yearFinalSum += subject.yearFinal.value;
|
||||
yearFinalCount++;
|
||||
yearSum += subject.yearFinal.value;
|
||||
yearCount++;
|
||||
}
|
||||
else if (subject.yearProposed != null && subject.yearProposed.value > 0) { // if final not available, add proposed to expected grades
|
||||
yearSum += subject.yearProposed.value;
|
||||
yearCount++;
|
||||
}
|
||||
else if (!Float.isNaN(subject.yearAverage)
|
||||
&& subject.yearAverage > 0
|
||||
&& !subject.isPointSubject) { // if final&proposed unavailable, calculate from avg
|
||||
yearSum += gradeFromAverage(subject.yearAverage);
|
||||
yearCount++;
|
||||
}
|
||||
if (subject.yearProposed != null && subject.yearProposed.value > 0) { // add proposed to proposed grades even if final is available
|
||||
yearProposedSum += subject.yearProposed.value;
|
||||
yearProposedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
String semester1ExpectedAverageStr = semester1Count > semester1ProposedCount || semester1Count > semester1FinalCount ? getString(R.string.dialog_averages_expected_format, 1, semester1Sum / semester1Count) : "";
|
||||
String semester1ProposedAverageStr = semester1ProposedCount > 0 ? getString(R.string.dialog_averages_proposed_format, 1, semester1ProposedSum / semester1ProposedCount) : "";
|
||||
String semester1FinalAverageStr = semester1FinalCount > 0 ? getString(R.string.dialog_averages_final_format, 1, semester1FinalSum / semester1FinalCount) : "";
|
||||
|
||||
String semester2ExpectedAverageStr = semester2Count > semester2ProposedCount || semester2Count > semester2FinalCount ? getString(R.string.dialog_averages_expected_format, 2, semester2Sum / semester2Count) : "";
|
||||
String semester2ProposedAverageStr = semester2ProposedCount > 0 ? getString(R.string.dialog_averages_proposed_format, 2, semester2ProposedSum / semester2ProposedCount) : "";
|
||||
String semester2FinalAverageStr = semester2FinalCount > 0 ? getString(R.string.dialog_averages_final_format, 2, semester2FinalSum / semester2FinalCount) : "";
|
||||
|
||||
String yearExpectedAverageStr = yearCount > yearProposedCount || yearCount > yearFinalCount ? getString(R.string.dialog_averages_expected_yearly_format, yearSum / yearCount) : "";
|
||||
String yearProposedAverageStr = yearProposedCount > 0 ? getString(R.string.dialog_averages_proposed_yearly_format, yearProposedSum / yearProposedCount) : "";
|
||||
String yearFinalAverageStr = yearFinalCount > 0 ? getString(R.string.dialog_averages_final_yearly_format, yearFinalSum / yearFinalCount) : "";
|
||||
|
||||
if (semester1ExpectedAverageStr.isEmpty() && semester1ProposedAverageStr.isEmpty() && semester1FinalAverageStr.isEmpty()) {
|
||||
semester1ExpectedAverageStr = getString(R.string.dialog_averages_unavailable_format, 1);
|
||||
}
|
||||
if (semester2ExpectedAverageStr.isEmpty() && semester2ProposedAverageStr.isEmpty() && semester2FinalAverageStr.isEmpty()) {
|
||||
semester2ExpectedAverageStr = getString(R.string.dialog_averages_unavailable_format, 2);
|
||||
}
|
||||
if (yearExpectedAverageStr.isEmpty() && yearProposedAverageStr.isEmpty() && yearFinalAverageStr.isEmpty()) {
|
||||
yearExpectedAverageStr = getString(R.string.dialog_averages_unavailable_yearly);
|
||||
}
|
||||
|
||||
new MaterialDialog.Builder(activity)
|
||||
.title(R.string.dialog_averages_title)
|
||||
.content(getString(
|
||||
R.string.dialog_averages_format,
|
||||
semester1ExpectedAverageStr,
|
||||
semester1ProposedAverageStr,
|
||||
semester1FinalAverageStr,
|
||||
semester2ExpectedAverageStr,
|
||||
semester2ProposedAverageStr,
|
||||
semester2FinalAverageStr,
|
||||
yearExpectedAverageStr,
|
||||
yearProposedAverageStr,
|
||||
yearFinalAverageStr))
|
||||
.positiveText(R.string.ok)
|
||||
.show();
|
||||
}
|
||||
}
|
@ -0,0 +1,324 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-3-4.
|
||||
*/
|
||||
|
||||
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.*
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.TARGET_GRADES_EDITOR
|
||||
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"
|
||||
}
|
||||
|
||||
private lateinit var app: App
|
||||
private lateinit var activity: MainActivity
|
||||
private lateinit var b: GradesFragmentBinding
|
||||
|
||||
private val job: Job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
// local/private variables go here
|
||||
private val adapter by lazy {
|
||||
GradesAdapter(activity)
|
||||
}
|
||||
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
|
||||
context ?: return null
|
||||
app = activity.application as App
|
||||
b = GradesFragmentBinding.inflate(inflater)
|
||||
b.refreshLayout.setParent(activity.swipeRefreshLayout)
|
||||
return b.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
if (!isAdded)
|
||||
return
|
||||
|
||||
expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L
|
||||
|
||||
app.db.gradeDao()
|
||||
.getAllOrderBy(App.profileId, app.gradesManager.getOrderByString())
|
||||
.observe(this, Observer { grades ->
|
||||
if (b.gradesRecyclerView.adapter == null) {
|
||||
b.gradesRecyclerView.adapter = adapter
|
||||
b.gradesRecyclerView.apply {
|
||||
setHasFixedSize(true)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
//addItemDecoration(SimpleDividerItemDecoration(context))
|
||||
}
|
||||
}
|
||||
|
||||
launch(Dispatchers.Default) {
|
||||
processGrades(grades)
|
||||
}
|
||||
|
||||
if (grades != null && grades.isNotEmpty()) {
|
||||
b.gradesRecyclerView.visibility = View.VISIBLE
|
||||
b.gradesNoData.visibility = View.GONE
|
||||
} else {
|
||||
b.gradesRecyclerView.visibility = View.GONE
|
||||
b.gradesNoData.visibility = View.VISIBLE
|
||||
}
|
||||
})
|
||||
|
||||
adapter.onGradeClick = {
|
||||
GradeDetailsDialog(activity, it)
|
||||
}
|
||||
|
||||
adapter.onGradesEditorClick = { subject, semester ->
|
||||
val otherSemester = subject.semesters.firstOrNull { it != semester }
|
||||
var gradeSumOtherSemester = otherSemester?.averages?.normalWeightedSum
|
||||
var gradeCountOtherSemester = otherSemester?.averages?.normalWeightedCount
|
||||
if (gradeSumOtherSemester ?: 0f == 0f || gradeCountOtherSemester ?: 0f == 0f) {
|
||||
gradeSumOtherSemester = otherSemester?.averages?.normalSum
|
||||
gradeCountOtherSemester = otherSemester?.averages?.normalCount?.toFloat()
|
||||
}
|
||||
|
||||
activity.loadTarget(TARGET_GRADES_EDITOR, Bundle(
|
||||
"subjectId" to subject.subjectId,
|
||||
"semester" to semester.number,
|
||||
"averageMode" to manager.yearAverageMode,
|
||||
"yearAverageBefore" to subject.averages.normalAvg,
|
||||
"gradeSumOtherSemester" to gradeSumOtherSemester,
|
||||
"gradeCountOtherSemester" to gradeCountOtherSemester,
|
||||
"averageOtherSemester" to otherSemester?.averages?.normalAvg,
|
||||
"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>()
|
||||
|
||||
var subjectId = -1L
|
||||
var semesterNumber = 0
|
||||
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
|
||||
for (grade in grades) {
|
||||
/*if (grade.parentId != null && grade.parentId != -1L)
|
||||
continue // the grade is hidden as a new, improved one is available*/
|
||||
if (grade.subjectId != subjectId) {
|
||||
subjectId = grade.subjectId
|
||||
semesterNumber = 0
|
||||
|
||||
subject = items.firstOrNull { it.subjectId == subjectId }
|
||||
?: GradesSubject(grade.subjectId, grade.subjectLongName ?: "").also {
|
||||
items += it
|
||||
it.semester = 2
|
||||
}
|
||||
}
|
||||
if (grade.semester != semesterNumber) {
|
||||
semesterNumber = grade.semester
|
||||
|
||||
semester = subject.semesters.firstOrNull { it.number == semesterNumber }
|
||||
?: 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
|
||||
Grade.TYPE_SEMESTER1_FINAL,
|
||||
Grade.TYPE_SEMESTER2_FINAL -> semester.finalGrade = grade
|
||||
Grade.TYPE_YEAR_PROPOSED -> subject.proposedGrade = grade
|
||||
Grade.TYPE_YEAR_FINAL -> subject.finalGrade = grade
|
||||
else -> {
|
||||
semester.grades += grade
|
||||
countGrade(grade, subject.averages)
|
||||
countGrade(grade, semester.averages)
|
||||
}
|
||||
}
|
||||
|
||||
subject.lastAddedDate = max(subject.lastAddedDate, grade.addedDate)
|
||||
}
|
||||
|
||||
val stats = GradesStats()
|
||||
|
||||
val sem1Expected = mutableListOf<Float>()
|
||||
val sem2Expected = mutableListOf<Float>()
|
||||
val yearlyExpected = mutableListOf<Float>()
|
||||
val sem1Proposed = mutableListOf<Float>()
|
||||
val sem2Proposed = mutableListOf<Float>()
|
||||
val yearlyProposed = mutableListOf<Float>()
|
||||
val sem1Final = mutableListOf<Float>()
|
||||
val sem2Final = mutableListOf<Float>()
|
||||
val yearlyFinal = mutableListOf<Float>()
|
||||
|
||||
val sem1Point = mutableListOf<Float>()
|
||||
val sem2Point = mutableListOf<Float>()
|
||||
val yearlyPoint = mutableListOf<Float>()
|
||||
|
||||
for (item in items) {
|
||||
item.semesters.forEach { sem ->
|
||||
manager.calculateAverages(sem.averages)
|
||||
if (sem.number == 1) {
|
||||
sem.proposedGrade?.value?.let { sem1Proposed += it }
|
||||
sem.finalGrade?.value?.let {
|
||||
sem1Final += it
|
||||
sem1Expected += it
|
||||
} ?: run {
|
||||
sem.averages.normalAvg?.let { sem1Expected += manager.getRoundedGrade(it).toFloat() }
|
||||
}
|
||||
sem.averages.pointAvgPercent?.let { sem1Point += it }
|
||||
}
|
||||
if (sem.number == 2) {
|
||||
sem.proposedGrade?.value?.let { sem2Proposed += it }
|
||||
sem.finalGrade?.value?.let {
|
||||
sem2Final += it
|
||||
sem2Expected += it
|
||||
} ?: run {
|
||||
sem.averages.normalAvg?.let { sem2Expected += manager.getRoundedGrade(it).toFloat() }
|
||||
}
|
||||
sem.averages.pointAvgPercent?.let { sem2Point += it }
|
||||
}
|
||||
}
|
||||
manager.calculateAverages(item.averages, item.semesters)
|
||||
item.proposedGrade?.value?.let { yearlyProposed += it }
|
||||
item.finalGrade?.value?.let {
|
||||
yearlyFinal += it
|
||||
yearlyExpected += it
|
||||
} ?: run {
|
||||
item.averages.normalAvg?.let { yearlyExpected += manager.getRoundedGrade(it).toFloat() }
|
||||
}
|
||||
item.averages.pointAvgPercent?.let { yearlyPoint += it }
|
||||
}
|
||||
|
||||
stats.normalSem1 = sem1Expected.averageOrNull()?.toFloat() ?: 0f
|
||||
stats.normalSem1Proposed = sem1Proposed.averageOrNull()?.toFloat() ?: 0f
|
||||
stats.normalSem1Final = sem1Final.averageOrNull()?.toFloat() ?: 0f
|
||||
stats.sem1NotAllFinal = sem1Final.size < sem1Expected.size
|
||||
stats.normalSem2 = sem2Expected.averageOrNull()?.toFloat() ?: 0f
|
||||
stats.normalSem2Proposed = sem2Proposed.averageOrNull()?.toFloat() ?: 0f
|
||||
stats.normalSem2Final = sem2Final.averageOrNull()?.toFloat() ?: 0f
|
||||
stats.sem2NotAllFinal = sem2Final.size < sem2Expected.size
|
||||
stats.normalYearly = yearlyExpected.averageOrNull()?.toFloat() ?: 0f
|
||||
stats.normalYearlyProposed = yearlyProposed.averageOrNull()?.toFloat() ?: 0f
|
||||
stats.normalYearlyFinal = yearlyFinal.averageOrNull()?.toFloat() ?: 0f
|
||||
stats.yearlyNotAllFinal = yearlyFinal.size < yearlyExpected.size
|
||||
|
||||
stats.pointSem1 = sem1Point.averageOrNull()?.toFloat() ?: 0f
|
||||
stats.pointSem2 = sem2Point.averageOrNull()?.toFloat() ?: 0f
|
||||
stats.pointYearly = yearlyPoint.averageOrNull()?.toFloat() ?: 0f
|
||||
|
||||
when (manager.orderBy) {
|
||||
GradesManager.ORDER_BY_DATE_DESC -> items.sortByDescending { it.lastAddedDate }
|
||||
GradesManager.ORDER_BY_DATE_ASC -> items.sortBy { it.lastAddedDate }
|
||||
}
|
||||
|
||||
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) {
|
||||
val value = manager.getGradeValue(grade)
|
||||
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
|
||||
}
|
||||
Grade.TYPE_POINT_AVG -> {
|
||||
averages.pointAvgSum += grade.value
|
||||
averages.pointAvgMax += grade.valueMax ?: value
|
||||
}
|
||||
Grade.TYPE_POINT_SUM -> {
|
||||
averages.pointSum += grade.value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,144 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.grades;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.Typeface;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.List;
|
||||
|
||||
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.ui.dialogs.grade.GradeDetailsDialog;
|
||||
import pl.szczodrzynski.edziennik.utils.Colors;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.COLOR_MODE_DEFAULT;
|
||||
|
||||
public class GradesListAdapter extends RecyclerView.Adapter<GradesListAdapter.ViewHolder> {
|
||||
private Context mContext;
|
||||
private List<GradeFull> gradeList;
|
||||
|
||||
//getting the context and product list with constructor
|
||||
public GradesListAdapter(Context mCtx, List<GradeFull> gradeList) {
|
||||
this.mContext = mCtx;
|
||||
this.gradeList = gradeList;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public GradesListAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
//inflating and returning our view holder
|
||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||
View view = inflater.inflate(R.layout.row_grades_list_item, parent, false);
|
||||
return new GradesListAdapter.ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull GradesListAdapter.ViewHolder holder, int position) {
|
||||
App app = (App) mContext.getApplicationContext();
|
||||
|
||||
GradeFull grade = gradeList.get(position);
|
||||
|
||||
holder.root.setOnClickListener((v -> {
|
||||
new GradeDetailsDialog(v.getContext(), App.Companion.getProfileId()).show(app, grade);
|
||||
}));
|
||||
|
||||
int gradeColor;
|
||||
if (App.Companion.getConfig().forProfile().getGrades().getColorMode() == COLOR_MODE_DEFAULT) {
|
||||
gradeColor = grade.color;
|
||||
}
|
||||
else {
|
||||
gradeColor = Colors.gradeToColor(grade);
|
||||
}
|
||||
|
||||
holder.gradesListName.setText(grade.name);
|
||||
holder.gradesListName.setSelected(true);
|
||||
holder.gradesListName.setTypeface(null, Typeface.BOLD);
|
||||
holder.gradesListName.setTextColor(ColorUtils.calculateLuminance(gradeColor) > 0.25 ? 0xff000000 : 0xffffffff);
|
||||
holder.gradesListName.getBackground().setColorFilter(new PorterDuffColorFilter(gradeColor, PorterDuff.Mode.MULTIPLY));
|
||||
|
||||
if (grade.description.trim().isEmpty()) {
|
||||
holder.gradesListDescription.setText(grade.category);
|
||||
holder.gradesListCategory.setText(grade.isImprovement ? app.getString(R.string.grades_improvement_category_format, "") : "");
|
||||
}
|
||||
else {
|
||||
holder.gradesListDescription.setText(grade.description);
|
||||
holder.gradesListCategory.setText(grade.isImprovement ? app.getString(R.string.grades_improvement_category_format, grade.category) : grade.category);
|
||||
}
|
||||
|
||||
DecimalFormat format = new DecimalFormat("#.##");
|
||||
DecimalFormat formatWithZeroes = new DecimalFormat("#.00");
|
||||
|
||||
if (grade.weight < 0) {
|
||||
grade.weight *= -1;
|
||||
}
|
||||
if (grade.type == Grade.TYPE_DESCRIPTIVE || grade.type == Grade.TYPE_DESCRIPTIVE_TEXT || grade.type == Grade.TYPE_TEXT || grade.type == Grade.TYPE_POINT_SUM) {
|
||||
holder.gradesListWeight.setVisibility(View.GONE);
|
||||
grade.weight = 0;
|
||||
}
|
||||
else {
|
||||
holder.gradesListWeight.setVisibility(View.VISIBLE);
|
||||
if (grade.type == Grade.TYPE_POINT_AVG) {
|
||||
holder.gradesListWeight.setText(app.getString(R.string.grades_max_points_format, format.format(grade.valueMax)));
|
||||
}
|
||||
else if (grade.weight == 0) {
|
||||
holder.gradesListWeight.setText(app.getString(R.string.grades_weight_not_counted));
|
||||
}
|
||||
else {
|
||||
holder.gradesListWeight.setText(app.getString(R.string.grades_weight_format, format.format(grade.weight) + (grade.classAverage != -1 ? ", " + formatWithZeroes.format(grade.classAverage) : "")));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
holder.gradesListTeacher.setText(grade.teacherFullName);
|
||||
holder.gradesListAddedDate.setText(Date.fromMillis(grade.addedDate).getFormattedStringShort());
|
||||
|
||||
if (!grade.seen) {
|
||||
holder.gradesListDescription.setBackground(mContext.getResources().getDrawable(R.drawable.bg_rounded_4dp));
|
||||
holder.gradesListDescription.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
|
||||
}
|
||||
else {
|
||||
holder.gradesListDescription.setBackground(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return gradeList.size();
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
View root;
|
||||
TextView gradesListName;
|
||||
TextView gradesListDescription;
|
||||
TextView gradesListCategory;
|
||||
TextView gradesListWeight;
|
||||
TextView gradesListTeacher;
|
||||
TextView gradesListAddedDate;
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
root = itemView.getRootView();
|
||||
gradesListName = itemView.findViewById(R.id.gradesListName);
|
||||
gradesListDescription = itemView.findViewById(R.id.gradesListCategoryColumn);
|
||||
gradesListCategory = itemView.findViewById(R.id.gradesListCategoryDescription);
|
||||
gradesListWeight = itemView.findViewById(R.id.gradesListWeight);
|
||||
gradesListTeacher = itemView.findViewById(R.id.gradesListTeacher);
|
||||
gradesListAddedDate = itemView.findViewById(R.id.gradesListAddedDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,787 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.grades;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.core.widget.NestedScrollView;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.mikepenz.iconics.view.IconicsImageView;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.MainActivity;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.data.db.AppDb;
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Subject;
|
||||
import pl.szczodrzynski.edziennik.data.db.full.GradeFull;
|
||||
import pl.szczodrzynski.edziennik.utils.Anim;
|
||||
import pl.szczodrzynski.edziennik.utils.Colors;
|
||||
import pl.szczodrzynski.edziennik.utils.Utils;
|
||||
import pl.szczodrzynski.edziennik.utils.models.ItemGradesSubjectModel;
|
||||
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
import static pl.szczodrzynski.edziennik.MainActivity.TARGET_GRADES_EDITOR;
|
||||
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.COLOR_MODE_DEFAULT;
|
||||
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.YEAR_1_AVG_2_SEM;
|
||||
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.YEAR_1_SEM_2_AVG;
|
||||
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.YEAR_1_SEM_2_SEM;
|
||||
|
||||
public class GradesSubjectAdapter extends ArrayAdapter<ItemGradesSubjectModel> implements View.OnClickListener {
|
||||
private static final String TAG = "GradesSubjectAdapter";
|
||||
private MainActivity activity;
|
||||
public List<ItemGradesSubjectModel> subjectList;
|
||||
|
||||
private void updateBadges(Context context, ItemGradesSubjectModel model) {
|
||||
// do not need this since we have an Observer for unread counters..
|
||||
//((App)getContext().getApplicationContext()).saveRegister(); // I don't like this.
|
||||
}
|
||||
|
||||
private boolean invalidAvg(float avg) {
|
||||
return avg == 0.0f || Float.isNaN(avg);
|
||||
}
|
||||
|
||||
private void updateSubjectSemesterBadges(ViewHolder holder, ItemGradesSubjectModel model) {
|
||||
if (model.semester1Unread > 0 || model.semester2Unread > 0) {
|
||||
holder.gradesSubjectTitle.setBackground(getContext().getResources().getDrawable(R.drawable.bg_rounded_4dp));
|
||||
holder.gradesSubjectTitle.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectTitle.setBackground(null);
|
||||
}
|
||||
if (model.semester1Unread > 0) {
|
||||
holder.gradesSubjectSemester1Title.setBackground(getContext().getResources().getDrawable(R.drawable.bg_rounded_4dp));
|
||||
holder.gradesSubjectSemester1Title.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester1Title.setBackground(null);
|
||||
}
|
||||
if (model.semester2Unread > 0) {
|
||||
holder.gradesSubjectSemester2Title.setBackground(getContext().getResources().getDrawable(R.drawable.bg_rounded_4dp));
|
||||
holder.gradesSubjectSemester2Title.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester2Title.setBackground(null);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean gradesSetAsRead(ViewHolder holder, ItemGradesSubjectModel model, int semester) {
|
||||
boolean somethingChanged = false;
|
||||
AppDb db = App.Companion.getDb();
|
||||
if (semester == 1) {
|
||||
model.semester1Unread = 0;
|
||||
for (GradeFull grade : model.grades1) {
|
||||
if (!grade.seen) {
|
||||
db.metadataDao().setSeen(App.Companion.getProfileId(), grade, somethingChanged = true);
|
||||
}
|
||||
}
|
||||
if (model.semester1Proposed != null && !model.semester1Proposed.seen)
|
||||
db.metadataDao().setSeen(App.Companion.getProfileId(), model.semester1Proposed, somethingChanged = true);
|
||||
if (model.semester1Final != null && !model.semester1Final.seen)
|
||||
db.metadataDao().setSeen(App.Companion.getProfileId(), model.semester1Final, somethingChanged = true);
|
||||
}
|
||||
else if (semester == 2) {
|
||||
model.semester2Unread = 0;
|
||||
for (GradeFull grade : model.grades2) {
|
||||
if (!grade.seen) {
|
||||
db.metadataDao().setSeen(App.Companion.getProfileId(), grade, somethingChanged = true);
|
||||
}
|
||||
}
|
||||
if (model.semester2Proposed != null && !model.semester2Proposed.seen)
|
||||
db.metadataDao().setSeen(App.Companion.getProfileId(), model.semester2Proposed, somethingChanged = true);
|
||||
if (model.semester2Final != null && !model.semester2Final.seen)
|
||||
db.metadataDao().setSeen(App.Companion.getProfileId(), model.semester2Final, somethingChanged = true);
|
||||
if (model.yearProposed != null && !model.yearProposed.seen)
|
||||
db.metadataDao().setSeen(App.Companion.getProfileId(), model.yearProposed, somethingChanged = true);
|
||||
if (model.yearFinal != null && !model.yearFinal.seen)
|
||||
db.metadataDao().setSeen(App.Companion.getProfileId(), model.yearFinal, somethingChanged = true);
|
||||
}
|
||||
if (somethingChanged) updateSubjectSemesterBadges(holder, model);
|
||||
return somethingChanged;
|
||||
}
|
||||
|
||||
private void expandSubject(ViewHolder holder, ItemGradesSubjectModel model) {
|
||||
Anim.fadeOut(holder.gradesSubjectPreviewContainer, 200, new Animation.AnimationListener() {
|
||||
@Override public void onAnimationStart(Animation animation) { }
|
||||
@Override public void onAnimationRepeat(Animation animation) { }
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
boolean somethingChanged = false;
|
||||
if (holder.gradesSubjectSemester1Container.getVisibility() != View.GONE) {
|
||||
somethingChanged = gradesSetAsRead(holder, model, 1);
|
||||
}
|
||||
if (holder.gradesSubjectSemester2Container.getVisibility() != View.GONE) {
|
||||
somethingChanged = gradesSetAsRead(holder, model, 2);
|
||||
}
|
||||
if (somethingChanged) updateBadges(getContext(), model);
|
||||
//holder.gradesSubjectPreviewContent.setVisibility(View.INVISIBLE);
|
||||
Anim.expand(holder.gradesSubjectContent, 500, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void collapseSubject(ViewHolder holder, ItemGradesSubjectModel model) {
|
||||
Anim.collapse(holder.gradesSubjectContent, 500, new Animation.AnimationListener() {
|
||||
@Override public void onAnimationStart(Animation animation) { }
|
||||
@Override public void onAnimationRepeat(Animation animation) { }
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
//holder.gradesSubjectPreviewContent.setVisibility(View.VISIBLE);
|
||||
Anim.fadeIn(holder.gradesSubjectPreviewContainer, 200, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
class BuildGradeViews extends AsyncTask<Void, Void, Void> {
|
||||
boolean findViews;
|
||||
ItemGradesSubjectModel model;
|
||||
//ViewGroup parent;
|
||||
//int position;
|
||||
ViewHolder holder;
|
||||
|
||||
BuildGradeViews(ItemGradesSubjectModel model, ViewHolder holder, ViewGroup parent, int position, boolean findViews) {
|
||||
this.model = model;
|
||||
this.holder = holder;
|
||||
this.findViews = findViews;
|
||||
//this.parent = parent;
|
||||
//this.position = position;
|
||||
}
|
||||
|
||||
protected Void doInBackground(Void... params) {
|
||||
if (this.findViews) {
|
||||
findViews(holder, holder.gradesSubjectRoot);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void onPostExecute(Void aVoid) {
|
||||
DecimalFormat df = new DecimalFormat("#.00");
|
||||
if (this.findViews) {
|
||||
// TODO NIE WIEM CO TO ROBI XD
|
||||
//this.viewHolder.semestrTitle1.setText(C0193R.string.semestr1);
|
||||
//this.viewHolder.semestrTitle2.setText(C0193R.string.semestr2);
|
||||
/*if (GradesSubjectAdapter.this.columns == 0) {
|
||||
DisplayMetrics metrics = GradeListAdapterBySubject.this.context.getResources().getDisplayMetrics();
|
||||
if (GradeListAdapterBySubject.this.context.getResources().getBoolean(C0193R.bool.tablet)) {
|
||||
GradeListAdapterBySubject.this.columns = ((int) ((((float) (this.parent.getWidth() / 2)) / metrics.density) - 30.0f)) / 58;
|
||||
} else {
|
||||
GradeListAdapterBySubject.this.columns = ((int) ((((float) this.parent.getWidth()) / metrics.density) - 30.0f)) / 58;
|
||||
}
|
||||
}
|
||||
this.viewHolder.semestrGridView1.setColumnCount(GradeListAdapterBySubject.this.columns);
|
||||
this.viewHolder.semestrGridView2.setColumnCount(GradeListAdapterBySubject.this.columns);*/
|
||||
}
|
||||
if (model != null && model.subject != null && model.subject.id != holder.lastSubject) {
|
||||
holder.gradesSubjectRoot.setBackground(Colors.getAdaptiveBackgroundDrawable(model.subject.color, model.subject.color));
|
||||
updateSubjectSemesterBadges(holder, model);
|
||||
holder.gradesSubjectTitle.setText(model.subject.longName);
|
||||
holder.gradesSubjectPreviewContent.removeAllViews();
|
||||
|
||||
if (model.expandView) {
|
||||
// commented is without animations, do not use now (with unread badges)
|
||||
//holder.gradesSubjectContent.setVisibility(View.VISIBLE);
|
||||
//holder.gradesSubjectPreviewContent.setVisibility(View.INVISIBLE);
|
||||
expandSubject(holder, model);
|
||||
model.expandView = false;
|
||||
}
|
||||
int showSemester = model.profile.getCurrentSemester();
|
||||
|
||||
List<GradeFull> gradeList = (showSemester == 1 ? model.grades1 : model.grades2);
|
||||
if (gradeList.size() == 0) {
|
||||
showSemester = (showSemester == 1 ? 2 : 1);
|
||||
gradeList = (showSemester == 1 ? model.grades1 : model.grades2);
|
||||
}
|
||||
|
||||
App app = (App) getContext().getApplicationContext();
|
||||
|
||||
float scale = getContext().getResources().getDisplayMetrics().density;
|
||||
int _5dp = (int) (5 * scale + 0.5f);
|
||||
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
|
||||
layoutParams.setMargins(0, 0, _5dp, 0);
|
||||
|
||||
DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
|
||||
int maxWidthPx = displayMetrics.widthPixels - Utils.dpToPx((app.getConfig().getUi().getMiniMenuVisible() ? 72 : 0)/*miniDrawer size*/ + 8 + 8/*left and right offsets*/ + 24/*ellipsize width*/);
|
||||
int totalWidthPx = 0;
|
||||
boolean ellipsized = false;
|
||||
|
||||
if (showSemester != model.profile.getCurrentSemester()) {
|
||||
// showing different semester, because of no grades in the selected one
|
||||
holder.gradesSubjectPreviewSemester.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectPreviewSemester.setText(getContext().getString(R.string.grades_semester_header_format, showSemester));
|
||||
// decrease the max preview width. DONE below
|
||||
/*holder.gradesSubjectPreviewSemester.measure(WRAP_CONTENT, WRAP_CONTENT);
|
||||
maxWidthPx -= holder.gradesSubjectPreviewSemester.getMeasuredWidth();
|
||||
maxWidthPx -= _5dp;*/
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectPreviewSemester.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
|
||||
if (model.grades1.size() > 0) {
|
||||
holder.gradesSubjectSemester1Nest.setNestedScrollingEnabled(false);
|
||||
holder.gradesSubjectSemester1Content.setHasFixedSize(false);
|
||||
holder.gradesSubjectSemester1Content.setNestedScrollingEnabled(false);
|
||||
holder.gradesSubjectSemester1Content.setLayoutManager(new LinearLayoutManager(activity));
|
||||
holder.gradesSubjectSemester1Content.setAdapter(new GradesListAdapter(activity, model.grades1));
|
||||
holder.gradesSubjectSemester1Header.setVisibility(View.VISIBLE);
|
||||
if (showSemester == 1) {
|
||||
holder.gradesSubjectSemester1Container.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester1Container.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (model.isDescriptiveSubject && !model.isNormalSubject && !model.isPointSubject && !model.isBehaviourSubject) {
|
||||
holder.gradesSubjectPreviewAverage.setVisibility(View.GONE);
|
||||
holder.gradesSubjectSemester1Average.setVisibility(View.GONE);
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectPreviewAverage.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectSemester1Average.setVisibility(View.VISIBLE);
|
||||
int formatSingle = (model.isPointSubject ? R.string.grades_average_single_percent_format : model.isBehaviourSubject ? R.string.grades_average_single_point_format : R.string.grades_average_single_format);
|
||||
int format = (model.isPointSubject ? R.string.grades_semester_average_percent_format : model.isBehaviourSubject ? R.string.grades_semester_average_point_format : R.string.grades_semester_average_format);
|
||||
// PREVIEW AVERAGE
|
||||
if (showSemester == 1) {
|
||||
holder.gradesSubjectPreviewAverage.setText(
|
||||
getContext().getString(
|
||||
formatSingle,
|
||||
invalidAvg(model.semester1Average) ? "-" : df.format(model.semester1Average)
|
||||
)
|
||||
);
|
||||
}
|
||||
// AVERAGE value
|
||||
holder.gradesSubjectSemester1Average.setText(
|
||||
getContext().getString(
|
||||
format,
|
||||
1,
|
||||
invalidAvg(model.semester1Average) ? "-" : df.format(model.semester1Average)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// PROPOSED grade
|
||||
if (model.semester1Proposed != null) {
|
||||
holder.gradesSubjectSemester1Proposed.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectSemester1Proposed.setText(model.semester1Proposed.name);
|
||||
holder.gradesSubjectSemester1Proposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester1Proposed.name), PorterDuff.Mode.MULTIPLY));
|
||||
holder.gradesSubjectSemester1Proposed.setTextColor(Colors.gradeNameToColor(model.semester1Proposed.name));
|
||||
if (showSemester == 1) {
|
||||
holder.gradesSubjectPreviewProposed.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectPreviewProposed.setText(model.semester1Proposed.name);
|
||||
holder.gradesSubjectPreviewProposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester1Proposed.name), PorterDuff.Mode.MULTIPLY));
|
||||
holder.gradesSubjectPreviewProposed.setTextColor(Colors.gradeNameToColor(model.semester1Proposed.name));
|
||||
}
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester1Proposed.setVisibility(View.GONE);
|
||||
if (showSemester == 1) {
|
||||
holder.gradesSubjectPreviewProposed.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
// FINAL grade
|
||||
if (model.semester1Final != null) {
|
||||
holder.gradesSubjectSemester1Final.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectSemester1Final.setText(model.semester1Final.name);
|
||||
holder.gradesSubjectSemester1Final.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester1Final.name), PorterDuff.Mode.MULTIPLY));
|
||||
if (showSemester == 1) {
|
||||
holder.gradesSubjectPreviewFinal.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectPreviewFinal.setText(model.semester1Final.name);
|
||||
holder.gradesSubjectPreviewFinal.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester1Final.name), PorterDuff.Mode.MULTIPLY));
|
||||
}
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester1Final.setVisibility(View.GONE);
|
||||
if (showSemester == 1) {
|
||||
holder.gradesSubjectPreviewFinal.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester1Header.setVisibility(View.GONE);
|
||||
holder.gradesSubjectSemester1Container.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (model.grades2.size() > 0) {
|
||||
holder.gradesSubjectSemester2Nest.setNestedScrollingEnabled(false);
|
||||
holder.gradesSubjectSemester2Content.setHasFixedSize(false);
|
||||
holder.gradesSubjectSemester2Content.setNestedScrollingEnabled(false);
|
||||
holder.gradesSubjectSemester2Content.setLayoutManager(new LinearLayoutManager(activity));
|
||||
holder.gradesSubjectSemester2Content.setAdapter(new GradesListAdapter(activity, model.grades2));
|
||||
holder.gradesSubjectSemester2Header.setVisibility(View.VISIBLE);
|
||||
if (showSemester == 2) {
|
||||
holder.gradesSubjectSemester2Container.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester2Container.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (model.isDescriptiveSubject && !model.isNormalSubject && !model.isPointSubject && !model.isBehaviourSubject) {
|
||||
holder.gradesSubjectPreviewAverage.setVisibility(View.GONE);
|
||||
holder.gradesSubjectSemester2Average.setVisibility(View.GONE);
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectPreviewAverage.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectSemester2Average.setVisibility(View.VISIBLE);
|
||||
// PREVIEW AVERAGE
|
||||
int formatDouble = (model.isPointSubject ? R.string.grades_average_double_percent_format : model.isBehaviourSubject ? R.string.grades_average_double_point_format : R.string.grades_average_double_format);
|
||||
int format = (model.isPointSubject ? R.string.grades_semester_average_percent_format : model.isBehaviourSubject ? R.string.grades_semester_average_point_format : R.string.grades_semester_average_format);
|
||||
if (showSemester == 2) {
|
||||
if (model.semester2Proposed != null || model.semester2Final != null) {
|
||||
holder.gradesSubjectPreviewAverage.setText(invalidAvg(model.semester2Average) ? "-" : df.format(model.semester2Average));
|
||||
holder.gradesSubjectPreviewYearAverage.setText(invalidAvg(model.yearAverage) ? "-" : df.format(model.yearAverage));
|
||||
holder.gradesSubjectPreviewYearAverage.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectPreviewAverage.setText(
|
||||
getContext().getString(
|
||||
formatDouble,
|
||||
invalidAvg(model.semester2Average) ? "-" : df.format(model.semester2Average),
|
||||
invalidAvg(model.yearAverage) ? "-" : df.format(model.yearAverage)
|
||||
)
|
||||
);
|
||||
holder.gradesSubjectPreviewYearAverage.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
// AVERAGE value
|
||||
holder.gradesSubjectSemester2Average.setText(
|
||||
getContext().getString(
|
||||
format,
|
||||
2,
|
||||
invalidAvg(model.semester2Average) ? "-" : df.format(model.semester2Average)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// PROPOSED grade
|
||||
if (model.semester2Proposed != null) {
|
||||
holder.gradesSubjectSemester2Proposed.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectSemester2Proposed.setText(model.semester2Proposed.name);
|
||||
holder.gradesSubjectSemester2Proposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester2Proposed.name), PorterDuff.Mode.MULTIPLY));
|
||||
holder.gradesSubjectSemester2Proposed.setTextColor(Colors.gradeNameToColor(model.semester2Proposed.name));
|
||||
if (showSemester == 2) {
|
||||
holder.gradesSubjectPreviewProposed.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectPreviewProposed.setText(model.semester2Proposed.name);
|
||||
holder.gradesSubjectPreviewProposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester2Proposed.name), PorterDuff.Mode.MULTIPLY));
|
||||
holder.gradesSubjectPreviewProposed.setTextColor(Colors.gradeNameToColor(model.semester2Proposed.name));
|
||||
}
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester2Proposed.setVisibility(View.GONE);
|
||||
if (showSemester == 2) {
|
||||
holder.gradesSubjectPreviewProposed.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
// FINAL grade
|
||||
if (model.semester2Final != null) {
|
||||
holder.gradesSubjectSemester2Final.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectSemester2Final.setText(model.semester2Final.name);
|
||||
holder.gradesSubjectSemester2Final.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester2Final.name), PorterDuff.Mode.MULTIPLY));
|
||||
if (showSemester == 2) {
|
||||
holder.gradesSubjectPreviewFinal.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectPreviewFinal.setText(model.semester2Final.name);
|
||||
holder.gradesSubjectPreviewFinal.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester2Final.name), PorterDuff.Mode.MULTIPLY));
|
||||
}
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester2Final.setVisibility(View.GONE);
|
||||
if (showSemester == 2) {
|
||||
holder.gradesSubjectPreviewFinal.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
if (model.isDescriptiveSubject && !model.isNormalSubject && !model.isPointSubject && !model.isBehaviourSubject) {
|
||||
holder.gradesSubjectYearAverage.setVisibility(View.GONE);
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectYearAverage.setVisibility(View.VISIBLE);
|
||||
// AVERAGE value
|
||||
int format = (model.isPointSubject ? R.string.grades_year_average_percent_format : model.isBehaviourSubject ? R.string.grades_year_average_point_format : R.string.grades_year_average_format);
|
||||
holder.gradesSubjectYearAverage.setText(
|
||||
getContext().getString(
|
||||
format,
|
||||
invalidAvg(model.yearAverage) ? "-" : df.format(model.yearAverage)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// PROPOSED grade
|
||||
if (model.yearProposed != null) {
|
||||
holder.gradesSubjectYearProposed.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectYearProposed.setText(model.yearProposed.name);
|
||||
holder.gradesSubjectYearProposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.yearProposed.name), PorterDuff.Mode.MULTIPLY));
|
||||
holder.gradesSubjectYearProposed.setTextColor(Colors.gradeNameToColor(model.yearProposed.name));
|
||||
if (showSemester == 2) {
|
||||
holder.gradesSubjectPreviewYearProposed.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectPreviewYearProposed.setText(model.yearProposed.name);
|
||||
holder.gradesSubjectPreviewYearProposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.yearProposed.name), PorterDuff.Mode.MULTIPLY));
|
||||
holder.gradesSubjectPreviewYearProposed.setTextColor(Colors.gradeNameToColor(model.yearProposed.name));
|
||||
}
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectYearProposed.setVisibility(View.GONE);
|
||||
if (showSemester == 2) {
|
||||
holder.gradesSubjectPreviewYearProposed.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
// FINAL grade
|
||||
if (model.yearFinal != null) {
|
||||
holder.gradesSubjectYearFinal.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectYearFinal.setText(model.yearFinal.name);
|
||||
holder.gradesSubjectYearFinal.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.yearFinal.name), PorterDuff.Mode.MULTIPLY));
|
||||
if (showSemester == 2) {
|
||||
holder.gradesSubjectPreviewYearFinal.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectPreviewYearFinal.setText(model.yearFinal.name);
|
||||
holder.gradesSubjectPreviewYearFinal.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.yearFinal.name), PorterDuff.Mode.MULTIPLY));
|
||||
}
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectYearFinal.setVisibility(View.GONE);
|
||||
if (showSemester == 2) {
|
||||
holder.gradesSubjectPreviewYearFinal.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester2Header.setVisibility(View.GONE);
|
||||
holder.gradesSubjectSemester2Container.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// decrease the width by average, proposed, final and semester TextViews
|
||||
holder.gradesSubjectPreviewContainer.measure(WRAP_CONTENT, MATCH_PARENT);
|
||||
//Log.d(TAG, "gradesSubjectPreviewContainer "+holder.gradesSubjectPreviewContainer.getMeasuredWidth());
|
||||
|
||||
/*holder.gradesSubjectPreviewAverage.measure(WRAP_CONTENT, WRAP_CONTENT);
|
||||
maxWidthPx -= holder.gradesSubjectPreviewAverage.getMeasuredWidth();
|
||||
maxWidthPx -= 2*_5dp;
|
||||
if (holder.gradesSubjectPreviewProposed.getVisibility() == View.VISIBLE) {
|
||||
holder.gradesSubjectPreviewProposed.measure(WRAP_CONTENT, WRAP_CONTENT);
|
||||
maxWidthPx -= holder.gradesSubjectPreviewProposed.getMeasuredWidth();
|
||||
maxWidthPx -= _5dp;
|
||||
}
|
||||
if (holder.gradesSubjectPreviewFinal.getVisibility() == View.VISIBLE) {
|
||||
holder.gradesSubjectPreviewFinal.measure(WRAP_CONTENT, WRAP_CONTENT);
|
||||
maxWidthPx -= holder.gradesSubjectPreviewFinal.getMeasuredWidth();
|
||||
maxWidthPx -= _5dp;
|
||||
}*/
|
||||
maxWidthPx -= holder.gradesSubjectPreviewContainer.getMeasuredWidth();
|
||||
maxWidthPx -= _5dp;
|
||||
|
||||
for (GradeFull grade: gradeList) {
|
||||
if (grade.semester != showSemester)
|
||||
continue;
|
||||
|
||||
int gradeColor;
|
||||
if (model.colorMode == COLOR_MODE_DEFAULT) {
|
||||
gradeColor = grade.color;
|
||||
} else {
|
||||
gradeColor = Colors.gradeToColor(grade);
|
||||
}
|
||||
|
||||
TextView gradeName = new TextView(activity);
|
||||
gradeName.setText(grade.name);
|
||||
gradeName.setTextColor(ColorUtils.calculateLuminance(gradeColor) > 0.25 ? 0xff000000 : 0xffffffff);
|
||||
gradeName.setPadding(_5dp, 0, _5dp, 0);
|
||||
gradeName.setBackgroundResource(R.drawable.bg_rounded_4dp);
|
||||
gradeName.getBackground().setColorFilter(new PorterDuffColorFilter(gradeColor, PorterDuff.Mode.MULTIPLY));
|
||||
gradeName.setTypeface(null, Typeface.BOLD);
|
||||
|
||||
gradeName.measure(WRAP_CONTENT, WRAP_CONTENT);
|
||||
totalWidthPx += gradeName.getMeasuredWidth() + _5dp;
|
||||
//Log.d(TAG, "totalWidthPx " + totalWidthPx);
|
||||
if (totalWidthPx >= maxWidthPx) {
|
||||
if (ellipsized)
|
||||
continue;
|
||||
ellipsized = true;
|
||||
TextView ellipsisText = new TextView(activity);
|
||||
ellipsisText.setText(R.string.ellipsis);
|
||||
ellipsisText.setTextAppearance(activity, R.style.NavView_TextView);
|
||||
ellipsisText.setTypeface(null, Typeface.BOLD);
|
||||
ellipsisText.setPadding(0, 0, 0, 0);
|
||||
holder.gradesSubjectPreviewContent.addView(ellipsisText, layoutParams);
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectPreviewContent.addView(gradeName, layoutParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (findViews) {
|
||||
//Log.d("GradesSubjectAdapter", "runOnUiThread");
|
||||
//this.viewHolder.gradesSubjectContent.setTag(View.VISIBLE);
|
||||
//this.viewHolder.gradesSubjectPreviewContainer.setTag(View.VISIBLE);
|
||||
activity.runOnUiThread(() -> {
|
||||
|
||||
holder.gradesSubjectRoot.setOnClickListener(v -> {
|
||||
if (holder.gradesSubjectContent.getVisibility() == View.GONE) {
|
||||
expandSubject(holder, model);
|
||||
}
|
||||
else {
|
||||
collapseSubject(holder, model);
|
||||
}
|
||||
});
|
||||
|
||||
holder.gradesSubjectSemester1Header.setOnClickListener(v -> {
|
||||
if (holder.gradesSubjectSemester1Container.getVisibility() == View.GONE) {
|
||||
if (gradesSetAsRead(holder, model, 1)) updateBadges(getContext(), model);
|
||||
|
||||
Anim.expand(holder.gradesSubjectSemester1Container, 500, null);
|
||||
if (holder.gradesSubjectSemester2Container.getVisibility() != View.GONE) {
|
||||
Anim.collapse(holder.gradesSubjectSemester2Container, 500, null);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Anim.collapse(holder.gradesSubjectSemester1Container, 500, null);
|
||||
}
|
||||
});
|
||||
holder.gradesSubjectSemester2Header.setOnClickListener(v -> {
|
||||
if (holder.gradesSubjectSemester2Container.getVisibility() == View.GONE) {
|
||||
if (gradesSetAsRead(holder, model, 2)) updateBadges(getContext(), model);
|
||||
|
||||
Anim.expand(holder.gradesSubjectSemester2Container, 500, null);
|
||||
if (holder.gradesSubjectSemester1Container.getVisibility() != View.GONE) {
|
||||
Anim.collapse(holder.gradesSubjectSemester1Container, 500, null);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Anim.collapse(holder.gradesSubjectSemester2Container, 500, null);
|
||||
}
|
||||
});
|
||||
|
||||
// hide the grade simulator when there are point, behaviour or descriptive grades
|
||||
if (model.isPointSubject || model.isBehaviourSubject || (model.isDescriptiveSubject && !model.isNormalSubject)) {
|
||||
holder.gradesSubjectSemester1EditButton.setVisibility(View.GONE);
|
||||
holder.gradesSubjectSemester2EditButton.setVisibility(View.GONE);
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester1EditButton.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectSemester1EditButton.setOnClickListener(v -> {
|
||||
Bundle arguments = new Bundle();
|
||||
|
||||
if (model.subject != null) {
|
||||
arguments.putLong("subjectId", model.subject.id);
|
||||
}
|
||||
arguments.putInt("semester", 1);
|
||||
//d(TAG, "Model is " + model);
|
||||
switch (model.yearAverageMode) {
|
||||
case YEAR_1_SEM_2_AVG:
|
||||
case YEAR_1_SEM_2_SEM:
|
||||
arguments.putInt("averageMode", -1);
|
||||
break;
|
||||
default:
|
||||
arguments.putInt("averageMode", model.semester2Final == null && model.yearAverageMode == YEAR_1_AVG_2_SEM ? -1 : model.yearAverageMode);
|
||||
arguments.putFloat("yearAverageBefore", model.yearAverage);
|
||||
arguments.putFloat("gradeSumOtherSemester", model.gradeSumSemester2);
|
||||
arguments.putFloat("gradeCountOtherSemester", model.gradeCountSemester2);
|
||||
arguments.putFloat("averageOtherSemester", model.semester2Average);
|
||||
arguments.putFloat("finalOtherSemester", model.semester2Final == null ? -1 : model.semester2Final.value);
|
||||
break;
|
||||
}
|
||||
|
||||
activity.loadTarget(TARGET_GRADES_EDITOR, arguments);
|
||||
});
|
||||
holder.gradesSubjectSemester2EditButton.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectSemester2EditButton.setOnClickListener(v -> {
|
||||
Bundle arguments = new Bundle();
|
||||
|
||||
if (model.subject != null) {
|
||||
arguments.putLong("subjectId", model.subject.id);
|
||||
}
|
||||
arguments.putInt("semester", 2);
|
||||
//d(TAG, "Model is " + model);
|
||||
switch (model.yearAverageMode) {
|
||||
case YEAR_1_AVG_2_SEM:
|
||||
case YEAR_1_SEM_2_SEM:
|
||||
arguments.putInt("averageMode", -1);
|
||||
break;
|
||||
default:
|
||||
arguments.putInt("averageMode", model.semester1Final == null && model.yearAverageMode == YEAR_1_SEM_2_AVG ? -1 : model.yearAverageMode);
|
||||
arguments.putFloat("yearAverageBefore", model.yearAverage);
|
||||
arguments.putFloat("gradeSumOtherSemester", model.gradeSumSemester1);
|
||||
arguments.putFloat("gradeCountOtherSemester", model.gradeCountSemester1);
|
||||
arguments.putFloat("averageOtherSemester", model.semester1Average);
|
||||
arguments.putFloat("finalOtherSemester", model.semester1Final == null ? -1 : model.semester1Final.value);
|
||||
break;
|
||||
}
|
||||
|
||||
activity.loadTarget(TARGET_GRADES_EDITOR, arguments);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
if (model != null && model.subject != null) {
|
||||
holder.lastSubject = model.subject.id;
|
||||
}
|
||||
super.onPostExecute(aVoid);
|
||||
}
|
||||
}
|
||||
|
||||
//getting the context and product list with constructor
|
||||
public GradesSubjectAdapter(List<ItemGradesSubjectModel> data, MainActivity context) {
|
||||
super(context, R.layout.row_grades_subject_item, data);
|
||||
this.activity = context;
|
||||
this.subjectList = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int position = (Integer) v.getTag();
|
||||
Object object = getItem(position);
|
||||
ItemGradesSubjectModel dataModel = (ItemGradesSubjectModel)object;
|
||||
|
||||
}
|
||||
|
||||
private void findViews(ViewHolder holder, View root) {
|
||||
//holder.gradesSubjectRoot = root.findViewById(R.id.gradesSubjectRoot);
|
||||
holder.gradesSubjectTitle = root.findViewById(R.id.gradesSubjectTitle);
|
||||
holder.gradesSubjectExpandIndicator = root.findViewById(R.id.gradesSubjectExpandIndicator);
|
||||
|
||||
holder.gradesSubjectPreviewContainer = root.findViewById(R.id.gradesSubjectPreviewContainer);
|
||||
holder.gradesSubjectPreviewSemester = root.findViewById(R.id.gradesSubjectPreviewSemester);
|
||||
holder.gradesSubjectPreviewContent = root.findViewById(R.id.gradesSubjectPreviewContent);
|
||||
holder.gradesSubjectPreviewAverage = root.findViewById(R.id.gradesSubjectPreviewAverage);
|
||||
holder.gradesSubjectPreviewProposed = root.findViewById(R.id.gradesSubjectPreviewProposed);
|
||||
holder.gradesSubjectPreviewFinal = root.findViewById(R.id.gradesSubjectPreviewFinal);
|
||||
holder.gradesSubjectPreviewYearAverage = root.findViewById(R.id.gradesSubjectPreviewYearAverage);
|
||||
holder.gradesSubjectPreviewYearProposed = root.findViewById(R.id.gradesSubjectPreviewYearProposed);
|
||||
holder.gradesSubjectPreviewYearFinal = root.findViewById(R.id.gradesSubjectPreviewYearFinal);
|
||||
|
||||
holder.gradesSubjectContent = root.findViewById(R.id.gradesSubjectContent);
|
||||
|
||||
holder.gradesSubjectSemester1Header = root.findViewById(R.id.gradesSubjectSemester1Header);
|
||||
holder.gradesSubjectSemester1Title = root.findViewById(R.id.gradesSubjectSemester1Title);
|
||||
holder.gradesSubjectSemester1ExpandIndicator = root.findViewById(R.id.gradesSubjectSemester1ExpandIndicator);
|
||||
holder.gradesSubjectSemester1Average = root.findViewById(R.id.gradesSubjectSemester1Average);
|
||||
holder.gradesSubjectSemester1Proposed = root.findViewById(R.id.gradesSubjectSemester1Proposed);
|
||||
holder.gradesSubjectSemester1Final = root.findViewById(R.id.gradesSubjectSemester1Final);
|
||||
holder.gradesSubjectSemester1EditButton = root.findViewById(R.id.gradesSubjectSemester1EditButton);
|
||||
holder.gradesSubjectSemester1Container = root.findViewById(R.id.gradesSubjectSemester1Container);
|
||||
holder.gradesSubjectSemester1Nest = root.findViewById(R.id.gradesSubjectSemester1Nest);
|
||||
holder.gradesSubjectSemester1Content = root.findViewById(R.id.gradesSubjectSemester1Content);
|
||||
|
||||
holder.gradesSubjectSemester2Header = root.findViewById(R.id.gradesSubjectSemester2Header);
|
||||
holder.gradesSubjectSemester2Title = root.findViewById(R.id.gradesSubjectSemester2Title);
|
||||
holder.gradesSubjectSemester2ExpandIndicator = root.findViewById(R.id.gradesSubjectSemester2ExpandIndicator);
|
||||
holder.gradesSubjectSemester2Average = root.findViewById(R.id.gradesSubjectSemester2Average);
|
||||
holder.gradesSubjectSemester2Proposed = root.findViewById(R.id.gradesSubjectSemester2Proposed);
|
||||
holder.gradesSubjectSemester2Final = root.findViewById(R.id.gradesSubjectSemester2Final);
|
||||
holder.gradesSubjectSemester2EditButton = root.findViewById(R.id.gradesSubjectSemester2EditButton);
|
||||
holder.gradesSubjectSemester2Container = root.findViewById(R.id.gradesSubjectSemester2Container);
|
||||
holder.gradesSubjectSemester2Nest = root.findViewById(R.id.gradesSubjectSemester2Nest);
|
||||
holder.gradesSubjectSemester2Content = root.findViewById(R.id.gradesSubjectSemester2Content);
|
||||
|
||||
holder.gradesSubjectYearAverage = root.findViewById(R.id.gradesSubjectYearAverage);
|
||||
holder.gradesSubjectYearProposed = root.findViewById(R.id.gradesSubjectYearProposed);
|
||||
holder.gradesSubjectYearFinal = root.findViewById(R.id.gradesSubjectYearFinal);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
|
||||
if (position >= subjectList.size()) {
|
||||
return convertView;
|
||||
}
|
||||
ItemGradesSubjectModel model = subjectList.get(position);
|
||||
if (model == null) {
|
||||
//Toast.makeText(activity, "return convertView;", Toast.LENGTH_SHORT).show();
|
||||
return convertView;
|
||||
}
|
||||
ViewHolder holder;
|
||||
if (convertView == null) {
|
||||
try {
|
||||
convertView = LayoutInflater.from(activity).inflate(R.layout.row_grades_subject_item, parent, false);
|
||||
holder = new ViewHolder();
|
||||
holder.gradesSubjectRoot = convertView.findViewById(R.id.gradesSubjectRoot);
|
||||
convertView.setTag(holder);
|
||||
new BuildGradeViews(model, holder, parent, position, true).execute();
|
||||
return convertView;
|
||||
} catch (Exception e) {
|
||||
return new View(getContext());
|
||||
}
|
||||
}
|
||||
holder = (ViewHolder) convertView.getTag();
|
||||
Subject subject = model.subject;
|
||||
holder.gradesSubjectTitle.setText(subject != null ? subject.longName : "");
|
||||
/*if (model.getNotSeen() > 0) {
|
||||
viewHolder.notseen.setVisibility(0);
|
||||
viewHolder.notseen.setText(model.getNotSeen() + "");
|
||||
} else {
|
||||
viewHolder.notseen.setVisibility(8);
|
||||
}*/
|
||||
new BuildGradeViews(model, holder, parent, position, false).execute();
|
||||
return convertView;
|
||||
}
|
||||
|
||||
public int getViewTypeCount() {
|
||||
return getCount();
|
||||
}
|
||||
|
||||
public int getItemViewType(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
class ViewHolder {
|
||||
long lastSubject;
|
||||
|
||||
TextView gradesSubjectTitle;
|
||||
ConstraintLayout gradesSubjectRoot;
|
||||
IconicsImageView gradesSubjectExpandIndicator;
|
||||
|
||||
LinearLayout gradesSubjectPreviewContainer;
|
||||
TextView gradesSubjectPreviewSemester;
|
||||
LinearLayout gradesSubjectPreviewContent;
|
||||
TextView gradesSubjectPreviewAverage;
|
||||
TextView gradesSubjectPreviewProposed;
|
||||
TextView gradesSubjectPreviewFinal;
|
||||
TextView gradesSubjectPreviewYearAverage;
|
||||
TextView gradesSubjectPreviewYearProposed;
|
||||
TextView gradesSubjectPreviewYearFinal;
|
||||
|
||||
LinearLayout gradesSubjectContent;
|
||||
|
||||
LinearLayout gradesSubjectSemester1Header;
|
||||
TextView gradesSubjectSemester1Title;
|
||||
IconicsImageView gradesSubjectSemester1ExpandIndicator;
|
||||
TextView gradesSubjectSemester1Average;
|
||||
TextView gradesSubjectSemester1Proposed;
|
||||
TextView gradesSubjectSemester1Final;
|
||||
IconicsImageView gradesSubjectSemester1EditButton;
|
||||
LinearLayout gradesSubjectSemester1Container;
|
||||
NestedScrollView gradesSubjectSemester1Nest;
|
||||
RecyclerView gradesSubjectSemester1Content;
|
||||
|
||||
LinearLayout gradesSubjectSemester2Header;
|
||||
TextView gradesSubjectSemester2Title;
|
||||
IconicsImageView gradesSubjectSemester2ExpandIndicator;
|
||||
TextView gradesSubjectSemester2Average;
|
||||
TextView gradesSubjectSemester2Proposed;
|
||||
TextView gradesSubjectSemester2Final;
|
||||
IconicsImageView gradesSubjectSemester2EditButton;
|
||||
LinearLayout gradesSubjectSemester2Container;
|
||||
NestedScrollView gradesSubjectSemester2Nest;
|
||||
RecyclerView gradesSubjectSemester2Content;
|
||||
|
||||
TextView gradesSubjectYearAverage;
|
||||
TextView gradesSubjectYearProposed;
|
||||
TextView gradesSubjectYearFinal;
|
||||
}
|
||||
}
|
@ -14,13 +14,13 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.YEAR_1_AVG_2_AVG
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.YEAR_1_AVG_2_SEM
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.YEAR_1_SEM_2_AVG
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.YEAR_ALL_GRADES
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentGradesEditorBinding
|
||||
import pl.szczodrzynski.edziennik.utils.Colors
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_AVG_2_AVG
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_AVG_2_SEM
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_SEM_2_AVG
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES
|
||||
import java.text.DecimalFormat
|
||||
import java.util.*
|
||||
import kotlin.math.floor
|
||||
|
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-2-29.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.grades.models
|
||||
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter.Companion.STATE_CLOSED
|
||||
|
||||
abstract class ExpandableItemModel<T>(val items: MutableList<T>) {
|
||||
open var level: Int = 3
|
||||
var state: Int = STATE_CLOSED
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-3-1.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.grades.models
|
||||
|
||||
class GradesAverages {
|
||||
var normalSum = 0f
|
||||
var normalCount = 0
|
||||
var normalWeightedSum = 0f
|
||||
var normalWeightedCount = 0f
|
||||
|
||||
var pointSum = 0f
|
||||
|
||||
var pointAvgSum = 0f
|
||||
var pointAvgMax = 0f
|
||||
|
||||
var normalAvg: Float? = null
|
||||
var pointAvgPercent: Float? = null
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-3-1.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.grades.models
|
||||
|
||||
class GradesEmpty
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-2-29.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.grades.models
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
||||
|
||||
data class GradesSemester(
|
||||
val subjectId: Long,
|
||||
val number: Int,
|
||||
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
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-3-3.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.grades.models
|
||||
|
||||
class GradesStats {
|
||||
var normalSem1 = 0f
|
||||
var normalSem1Proposed = 0f
|
||||
var normalSem1Final = 0f
|
||||
var normalSem2 = 0f
|
||||
var normalSem2Proposed = 0f
|
||||
var normalSem2Final = 0f
|
||||
var normalYearly = 0f
|
||||
var normalYearlyProposed = 0f
|
||||
var normalYearlyFinal = 0f
|
||||
|
||||
var sem1NotAllFinal = false
|
||||
var sem2NotAllFinal = false
|
||||
var yearlyNotAllFinal = false
|
||||
|
||||
var pointSem1 = 0f
|
||||
var pointSem2 = 0f
|
||||
var pointYearly = 0f
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-2-29.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.grades.models
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
||||
|
||||
data class GradesSubject(
|
||||
val subjectId: Long,
|
||||
val subjectName: String,
|
||||
val semesters: MutableList<GradesSemester> = mutableListOf()
|
||||
) : ExpandableItemModel<GradesSemester>(semesters) {
|
||||
override var level = 1
|
||||
|
||||
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
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-2-29.
|
||||
*/
|
||||
|
||||
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, adapter: GradesAdapter)
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-3-1.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.grades.viewholder
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
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(
|
||||
inflater: LayoutInflater,
|
||||
parent: ViewGroup,
|
||||
val b: GradesItemEmptyBinding = GradesItemEmptyBinding.inflate(inflater, parent, false)
|
||||
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<GradesEmpty> {
|
||||
companion object {
|
||||
private const val TAG = "EmptyViewHolder"
|
||||
}
|
||||
|
||||
override fun onBind(activity: AppCompatActivity, app: App, item: GradesEmpty, position: Int, adapter: GradesAdapter) {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-2-29.
|
||||
*/
|
||||
|
||||
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(
|
||||
inflater: LayoutInflater,
|
||||
parent: ViewGroup,
|
||||
val b: GradesItemGradeBinding = GradesItemGradeBinding.inflate(inflater, parent, false)
|
||||
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<GradeFull> {
|
||||
companion object {
|
||||
private const val TAG = "GradeViewHolder"
|
||||
}
|
||||
|
||||
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
|
||||
override fun onBind(activity: AppCompatActivity, app: App, grade: GradeFull, position: Int, adapter: GradesAdapter) {
|
||||
val manager = app.gradesManager
|
||||
|
||||
b.gradeName.setGrade(grade, manager, bigView = true)
|
||||
|
||||
if (grade.description.isNullOrBlank()) {
|
||||
b.gradeDescription.text = grade.category
|
||||
b.gradeCategory.text =
|
||||
if (grade.isImprovement)
|
||||
app.getString(R.string.grades_improvement_category_format, "")
|
||||
else
|
||||
""
|
||||
} else {
|
||||
b.gradeDescription.text = grade.description
|
||||
b.gradeCategory.text =
|
||||
if (grade.isImprovement)
|
||||
app.getString(R.string.grades_improvement_category_format, grade.category)
|
||||
else
|
||||
grade.category
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-2-29.
|
||||
*/
|
||||
|
||||
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.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,
|
||||
parent: ViewGroup,
|
||||
val b: GradesItemSemesterBinding = GradesItemSemesterBinding.inflate(inflater, parent, false)
|
||||
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<GradesSemester> {
|
||||
companion object {
|
||||
private const val TAG = "SemesterViewHolder"
|
||||
}
|
||||
|
||||
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) {
|
||||
GradesAdapter.STATE_CLOSED -> 0f
|
||||
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)
|
||||
}
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-3-3.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.grades.viewholder
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
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
|
||||
|
||||
class StatsViewHolder(
|
||||
inflater: LayoutInflater,
|
||||
parent: ViewGroup,
|
||||
val b: GradesItemStatsBinding = GradesItemStatsBinding.inflate(inflater, parent, false)
|
||||
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<GradesStats> {
|
||||
companion object {
|
||||
private const val TAG = "StatsViewHolder"
|
||||
}
|
||||
|
||||
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>()
|
||||
|
||||
getSemesterString(app, item.normalSem1, item.normalSem1Proposed, item.normalSem1Final, item.sem1NotAllFinal).let { (average, notice) ->
|
||||
b.normalSemester1Layout.isVisible = average != null
|
||||
b.normalSemester1Notice.isVisible = notice != null
|
||||
b.normalSemester1.text = average
|
||||
b.normalSemester1Notice.text = notice
|
||||
if (average != null)
|
||||
showAverages += 1
|
||||
}
|
||||
getSemesterString(app, item.normalSem2, item.normalSem2Proposed, item.normalSem2Final, item.sem2NotAllFinal).let { (average, notice) ->
|
||||
b.normalSemester2Layout.isVisible = average != null
|
||||
b.normalSemester2Notice.isVisible = notice != null
|
||||
b.normalSemester2.text = average
|
||||
b.normalSemester2Notice.text = notice
|
||||
if (average != null)
|
||||
showAverages += 2
|
||||
}
|
||||
getSemesterString(app, item.normalYearly, item.normalYearlyProposed, item.normalYearlyFinal, item.yearlyNotAllFinal).let { (average, notice) ->
|
||||
b.normalYearlyLayout.isVisible = average != null
|
||||
b.normalYearlyNotice.isVisible = notice != null
|
||||
b.normalYearly.text = average
|
||||
b.normalYearlyNotice.text = notice
|
||||
if (average != null)
|
||||
showAverages += 3
|
||||
}
|
||||
|
||||
b.normalTitle.isVisible = showAverages.size > 0
|
||||
b.normalLayout.isVisible = showAverages.size > 0
|
||||
b.normalDivider.isVisible = showAverages.size > 0
|
||||
b.helpButton.isVisible = showAverages.size > 0
|
||||
b.normalDiv1.isVisible = showAverages.contains(1) && showAverages.contains(2) || showAverages.contains(1) && showAverages.contains(3)
|
||||
b.normalDiv2.isVisible = showAverages.contains(2) && showAverages.contains(3)
|
||||
|
||||
getSemesterString(app, 0f, 0f, item.pointSem1, false).let { (average, _) ->
|
||||
b.pointSemester1Layout.isVisible = average != null
|
||||
b.pointSemester1.text = average
|
||||
if (average != null)
|
||||
showPoint += 1
|
||||
}
|
||||
getSemesterString(app, 0f, 0f, item.pointSem2, false).let { (average, _) ->
|
||||
b.pointSemester2Layout.isVisible = average != null
|
||||
b.pointSemester2.text = average
|
||||
if (average != null)
|
||||
showPoint += 2
|
||||
}
|
||||
getSemesterString(app, 0f, 0f, item.pointYearly, false).let { (average, _) ->
|
||||
b.pointYearlyLayout.isVisible = average != null
|
||||
b.pointYearly.text = average
|
||||
if (average != null)
|
||||
showPoint += 3
|
||||
}
|
||||
|
||||
b.pointTitle.isVisible = showPoint.size > 0
|
||||
b.pointLayout.isVisible = showPoint.size > 0
|
||||
b.pointDivider.isVisible = showPoint.size > 0
|
||||
b.pointDiv1.isVisible = showPoint.contains(1) && showPoint.contains(2)
|
||||
b.pointDiv2.isVisible = showPoint.contains(2) && showPoint.contains(3)
|
||||
|
||||
b.noData.isVisible = showAverages.isEmpty() && showPoint.isEmpty()
|
||||
b.disclaimer.isVisible = !b.noData.isVisible
|
||||
|
||||
b.helpButton.onClick {
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.grades_stats_help_title)
|
||||
.setMessage(R.string.grades_stats_help_text)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
b.customValueDivider.isVisible = manager.plusValue != null || manager.minusValue != null
|
||||
b.customValueLayout.isVisible = b.customValueDivider.isVisible
|
||||
b.customValueButton.onClick {
|
||||
GradesConfigDialog(activity, reloadOnDismiss = true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSemesterString(context: Context, expected: Float, proposed: Float, final: Float, notAllFinal: Boolean) : Pair<String?, String?> {
|
||||
val format = DecimalFormat("#.00")
|
||||
|
||||
val average = when {
|
||||
final != 0f -> final
|
||||
proposed != 0f -> proposed
|
||||
expected != 0f -> expected
|
||||
else -> null
|
||||
}?.let {
|
||||
format.format(it)
|
||||
}
|
||||
|
||||
val notice = when {
|
||||
final != 0f -> when {
|
||||
notAllFinal -> if (expected != 0f)
|
||||
context.getString(R.string.grades_stats_from_final, format.format(expected))
|
||||
else
|
||||
context.getString(R.string.grades_stats_from_final_no_expected)
|
||||
proposed != 0f -> context.getString(R.string.grades_stats_proposed_avg, format.format(proposed))
|
||||
else -> null
|
||||
}
|
||||
proposed != 0f -> if (expected != 0f)
|
||||
context.getString(R.string.grades_stats_from_proposed, format.format(expected))
|
||||
else
|
||||
context.getString(R.string.grades_stats_from_proposed_no_expected)
|
||||
expected != 0f -> context.getString(R.string.grades_stats_expected)
|
||||
else -> null
|
||||
}
|
||||
|
||||
return average to notice
|
||||
}
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-2-29.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.grades.viewholder
|
||||
|
||||
import android.text.TextUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
import android.widget.LinearLayout
|
||||
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
|
||||
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
|
||||
|
||||
class SubjectViewHolder(
|
||||
inflater: LayoutInflater,
|
||||
parent: ViewGroup,
|
||||
val b: GradesItemSubjectBinding = GradesItemSubjectBinding.inflate(inflater, parent, false)
|
||||
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<GradesSubject> {
|
||||
companion object {
|
||||
private const val TAG = "SubjectViewHolder"
|
||||
}
|
||||
|
||||
override fun onBind(activity: AppCompatActivity, app: App, item: GradesSubject, position: Int, adapter: GradesAdapter) {
|
||||
val manager = app.gradesManager
|
||||
val contextWrapper = ContextThemeWrapper(activity, Themes.themeInt)
|
||||
|
||||
b.subjectName.text = item.subjectName
|
||||
b.dropdownIcon.rotation = when (item.state) {
|
||||
STATE_CLOSED -> 0f
|
||||
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
|
||||
|
||||
val gradesContainer = b.previewContainer[0]
|
||||
b.previewContainer.removeAllViews()
|
||||
b.gradesContainer.removeAllViews()
|
||||
b.previewContainer.addView(gradesContainer)
|
||||
|
||||
val firstSemester = item.semesters.firstOrNull() ?: return
|
||||
|
||||
b.yearSummary.text = manager.getYearSummaryString(app, item.semesters.map { it.grades.size }.sum(), item.averages)
|
||||
|
||||
if (firstSemester.number != item.semester) {
|
||||
b.gradesContainer.addView(TextView(contextWrapper).apply {
|
||||
setText(R.string.grades_preview_other_semester, firstSemester.number)
|
||||
setPadding(0, 0, 5.dp, 0)
|
||||
maxLines = 1
|
||||
ellipsize = TextUtils.TruncateAt.END
|
||||
})
|
||||
}
|
||||
|
||||
/*if (firstSemester.grades.isEmpty()) {
|
||||
b.previewContainer.addView(TextView(app).apply {
|
||||
setText(R.string.grades_no_grades_in_semester, firstSemester.number)
|
||||
})
|
||||
}*/
|
||||
|
||||
val hideImproved = manager.hideImproved
|
||||
for (grade in firstSemester.grades) {
|
||||
if (hideImproved && grade.isImproved)
|
||||
continue
|
||||
b.gradesContainer.addView(GradeView(
|
||||
contextWrapper,
|
||||
grade,
|
||||
manager,
|
||||
periodGradesTextual = false
|
||||
))
|
||||
}
|
||||
|
||||
b.previewContainer.addView(TextView(contextWrapper).apply {
|
||||
text = manager.getAverageString(app, firstSemester.averages, nameSemester = true, showSemester = firstSemester.number)
|
||||
//gravity = Gravity.END
|
||||
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
|
||||
setMargins(0, 0, 8.dp, 0)
|
||||
}
|
||||
maxLines = 1
|
||||
ellipsize = TextUtils.TruncateAt.END
|
||||
})
|
||||
|
||||
firstSemester.proposedGrade?.let {
|
||||
b.previewContainer.addView(GradeView(
|
||||
contextWrapper,
|
||||
it,
|
||||
manager
|
||||
))
|
||||
}
|
||||
firstSemester.finalGrade?.let {
|
||||
b.previewContainer.addView(GradeView(
|
||||
contextWrapper,
|
||||
it,
|
||||
manager
|
||||
))
|
||||
}
|
||||
|
||||
if (firstSemester.number == item.semester) {
|
||||
b.previewContainer.addView(TextView(contextWrapper).apply {
|
||||
text = manager.getAverageString(app, item.averages, nameSemester = true)
|
||||
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
|
||||
setMargins(0, 0, 8.dp, 0)
|
||||
}
|
||||
maxLines = 1
|
||||
ellipsize = TextUtils.TruncateAt.END
|
||||
})
|
||||
|
||||
item.proposedGrade?.let {
|
||||
b.previewContainer.addView(GradeView(
|
||||
contextWrapper,
|
||||
it,
|
||||
manager
|
||||
))
|
||||
}
|
||||
item.finalGrade?.let {
|
||||
b.previewContainer.addView(GradeView(
|
||||
contextWrapper,
|
||||
it,
|
||||
manager
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
@ -57,4 +57,4 @@ class HomeDummyCard(override val id: Int) : HomeCard, CoroutineScope {
|
||||
timer?.cancel()
|
||||
timer = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,10 +26,7 @@ import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentHomeBinding
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.home.StudentNumberDialog
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.cards.HomeDebugCard
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.cards.HomeGradesCard
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.cards.HomeLuckyNumberCard
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.cards.HomeTimetableCard
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.cards.*
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
|
||||
@ -40,16 +37,21 @@ class HomeFragment : Fragment(), CoroutineScope {
|
||||
private const val TAG = "HomeFragment"
|
||||
|
||||
fun swapCards(fromPosition: Int, toPosition: Int, cardAdapter: HomeCardAdapter) {
|
||||
val homeCards = App.config.ui.homeCards.toMutableList()
|
||||
val fromPair = homeCards[fromPosition]
|
||||
homeCards[fromPosition] = homeCards[toPosition]
|
||||
homeCards[toPosition] = fromPair
|
||||
App.config.ui.homeCards = homeCards
|
||||
|
||||
val fromCard = cardAdapter.items[fromPosition]
|
||||
val toCard = cardAdapter.items[toPosition]
|
||||
if (fromCard.id == 100 || toCard.id == 100) {
|
||||
// debug card is not swappable
|
||||
return
|
||||
}
|
||||
cardAdapter.items[fromPosition] = cardAdapter.items[toPosition]
|
||||
cardAdapter.items[toPosition] = fromCard
|
||||
cardAdapter.notifyItemMoved(fromPosition, toPosition)
|
||||
|
||||
val homeCards = App.config.forProfile().ui.homeCards.toMutableList()
|
||||
val fromPair = homeCards[fromPosition]
|
||||
homeCards[fromPosition] = homeCards[toPosition]
|
||||
homeCards[toPosition] = fromPair
|
||||
App.config.forProfile().ui.homeCards = homeCards
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,15 +109,15 @@ class HomeFragment : Fragment(), CoroutineScope {
|
||||
|
||||
val showUnified = false
|
||||
|
||||
val cards = app.config.ui.homeCards.filter { it.profileId == app.profile.id }.toMutableList()
|
||||
val cards = app.config.forProfile().ui.homeCards.filter { it.profileId == app.profile.id }.toMutableList()
|
||||
if (cards.isEmpty()) {
|
||||
cards += listOf(
|
||||
HomeCardModel(app.profile.id, HomeCard.CARD_LUCKY_NUMBER),
|
||||
HomeCardModel(app.profile.id, HomeCard.CARD_TIMETABLE),
|
||||
/*HomeCardModel(app.profile.id, HomeCard.CARD_EVENTS),*/
|
||||
HomeCardModel(app.profile.id, HomeCard.CARD_EVENTS),
|
||||
HomeCardModel(app.profile.id, HomeCard.CARD_GRADES)
|
||||
)
|
||||
app.config.ui.homeCards = app.config.ui.homeCards.toMutableList().also { it.addAll(cards) }
|
||||
app.config.forProfile().ui.homeCards = app.config.forProfile().ui.homeCards.toMutableList().also { it.addAll(cards) }
|
||||
}
|
||||
|
||||
val items = mutableListOf<HomeCard>()
|
||||
@ -124,6 +126,7 @@ class HomeFragment : Fragment(), CoroutineScope {
|
||||
HomeCard.CARD_LUCKY_NUMBER -> HomeLuckyNumberCard(it.cardId, app, activity, this, app.profile)
|
||||
HomeCard.CARD_TIMETABLE -> HomeTimetableCard(it.cardId, app, activity, this, app.profile)
|
||||
HomeCard.CARD_GRADES -> HomeGradesCard(it.cardId, app, activity, this, app.profile)
|
||||
HomeCard.CARD_EVENTS -> HomeEventsCard(it.cardId, app, activity, this, app.profile)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-2-28.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.home.cards
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.view.plusAssign
|
||||
import androidx.core.view.setMargins
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.databinding.CardHomeEventsBinding
|
||||
import pl.szczodrzynski.edziennik.dp
|
||||
import pl.szczodrzynski.edziennik.onClick
|
||||
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.home.HomeCard
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
|
||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class HomeEventsCard(
|
||||
override val id: Int,
|
||||
val app: App,
|
||||
val activity: MainActivity,
|
||||
val fragment: HomeFragment,
|
||||
val profile: Profile
|
||||
) : HomeCard, CoroutineScope {
|
||||
companion object {
|
||||
private const val TAG = "HomeEventsCard"
|
||||
}
|
||||
|
||||
private var job: Job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
private lateinit var adapter: EventListAdapter
|
||||
|
||||
override fun bind(position: Int, holder: HomeCardAdapter.ViewHolder) { launch {
|
||||
holder.root.removeAllViews()
|
||||
val b = CardHomeEventsBinding.inflate(LayoutInflater.from(holder.root.context))
|
||||
b.root.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply {
|
||||
setMargins(8.dp)
|
||||
}
|
||||
holder.root += b.root
|
||||
|
||||
adapter = EventListAdapter(
|
||||
activity,
|
||||
simpleMode = true,
|
||||
showDate = true,
|
||||
showWeekDay = true,
|
||||
onItemClick = {
|
||||
EventDetailsDialog(
|
||||
activity,
|
||||
it
|
||||
)
|
||||
},
|
||||
onEventEditClick = {
|
||||
EventManualDialog(
|
||||
activity,
|
||||
it.profileId,
|
||||
editingEvent = it
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
app.db.eventDao().getAllNearest(App.profileId, Date.getToday(), 4).observe(activity, Observer { events ->
|
||||
adapter.items = events
|
||||
if (b.eventsView.adapter == null) {
|
||||
b.eventsView.adapter = adapter
|
||||
b.eventsView.apply {
|
||||
isNestedScrollingEnabled = false
|
||||
setHasFixedSize(true)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
addItemDecoration(SimpleDividerItemDecoration(context))
|
||||
}
|
||||
}
|
||||
adapter.notifyDataSetChanged()
|
||||
|
||||
if (events != null && events.isNotEmpty()) {
|
||||
b.eventsView.visibility = View.VISIBLE
|
||||
b.eventsNoData.visibility = View.GONE
|
||||
} else {
|
||||
b.eventsView.visibility = View.GONE
|
||||
b.eventsNoData.visibility = View.VISIBLE
|
||||
}
|
||||
})
|
||||
|
||||
holder.root.onClick {
|
||||
activity.loadTarget(MainActivity.DRAWER_ITEM_AGENDA)
|
||||
}
|
||||
}}
|
||||
|
||||
override fun unbind(position: Int, holder: HomeCardAdapter.ViewHolder) = Unit
|
||||
}
|
@ -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,16 +25,15 @@ 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.*
|
||||
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.models.ItemGradesSubjectModel
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
@ -121,32 +117,11 @@ class HomeGradesCard(
|
||||
16 /*ellipsize width*/)) / 1.5f
|
||||
|
||||
subject.grades1.onEach { grade ->
|
||||
val gradeColor = when (App.config.forProfile().grades.colorMode) {
|
||||
Profile.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
|
||||
|
||||
|
@ -156,6 +156,7 @@ class HomeTimetableCard(
|
||||
|| (it.displayDate == today
|
||||
&& it.displayEndTime != null
|
||||
&& it.displayEndTime!! >= now))
|
||||
&& !it.isCancelled
|
||||
}) && checkedDays < 7) {
|
||||
|
||||
timetableDate.stepForward(0, 0, 1)
|
||||
@ -165,8 +166,8 @@ class HomeTimetableCard(
|
||||
&& !(it.isCancelled && ignoreCancelled)
|
||||
}
|
||||
|
||||
if (lessons.isEmpty() && timetableDate.weekDay <= 5)
|
||||
break
|
||||
//if (lessons.isEmpty() && timetableDate.weekDay <= 5)
|
||||
// break
|
||||
|
||||
checkedDays++
|
||||
}
|
||||
@ -208,6 +209,8 @@ class HomeTimetableCard(
|
||||
return@launch
|
||||
}
|
||||
|
||||
lessons = lessons.filter { it.type != Lesson.TYPE_NO_LESSONS }
|
||||
|
||||
b.timetableLayout.visibility = View.VISIBLE
|
||||
b.noTimetableLayout.visibility = View.GONE
|
||||
b.noLessonsLayout.visibility = View.GONE
|
||||
@ -246,7 +249,7 @@ class HomeTimetableCard(
|
||||
|
||||
subjectSpannable = firstLesson.subjectSpannable
|
||||
|
||||
counterJob = startCoroutineTimer(repeatMillis = 1000) {
|
||||
counterJob = startCoroutineTimer(repeatMillis = 500) {
|
||||
count()
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user