mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-06-22 10:03:03 +02:00
Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
da48c059ec | |||
ee5566d1ef | |||
b794b30346 | |||
0db6393bb0 | |||
fcc3c55110 | |||
328c07eaf4 | |||
b004ec048e | |||
b9f83875a0 | |||
8c869d082b | |||
043f8210ba | |||
41a79caf83 | |||
0427fa6087 | |||
2f3c912dbe |
1
annotation/.gitignore
vendored
Normal file
1
annotation/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/build
|
29
annotation/build.gradle
Normal file
29
annotation/build.gradle
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-3-28.
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply plugin: 'java-library'
|
||||||
|
apply plugin: 'kotlin'
|
||||||
|
apply plugin: 'kotlin-kapt'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceCompatibility = "7"
|
||||||
|
targetCompatibility = "7"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
compileKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileTestKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-3-28.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.annotation
|
||||||
|
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
|
@MustBeDocumented
|
||||||
|
annotation class SelectiveDao(
|
||||||
|
val db: KClass<*>
|
||||||
|
)
|
@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-3-28.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.annotation
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
|
@MustBeDocumented
|
||||||
|
annotation class UpdateSelective(
|
||||||
|
val primaryKeys: Array<String>,
|
||||||
|
val skippedColumns: Array<String> = []
|
||||||
|
)
|
@ -193,6 +193,9 @@ dependencies {
|
|||||||
implementation "io.coil-kt:coil:0.9.2"
|
implementation "io.coil-kt:coil:0.9.2"
|
||||||
|
|
||||||
implementation 'com.github.kuba2k2:NumberSlidingPicker:2921225f76'
|
implementation 'com.github.kuba2k2:NumberSlidingPicker:2921225f76'
|
||||||
|
|
||||||
|
implementation project(":annotation")
|
||||||
|
kapt project(":codegen")
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@ -1,4 +1,10 @@
|
|||||||
<h3>Wersja 4.0-rc.3, 2020-03-29</h3>
|
<h3>Wersja 4.0-rc.4, 2020-03-30</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Poprawione pobieranie załączników w Librusie.</li>
|
||||||
|
<li>Nowy widok zadań domowych</li>
|
||||||
|
<li>Możliwość oznaczania zadań domowych i wydarzeń jako wykonane.</li>
|
||||||
|
</ul>
|
||||||
|
<!--<h3>Wersja 4.0-rc.3, 2020-03-29</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<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><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><b>Przebudowaliśmy cały moduł synchronizacji</b>, co oznacza większą stabilność aplikacji, szybkość 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>
|
||||||
@ -22,7 +28,7 @@
|
|||||||
<li>Poprawiliśmy synchronizację w tle na niektórych telefonach</li>
|
<li>Poprawiliśmy synchronizację w tle na niektórych telefonach</li>
|
||||||
<li>Usunąłem denerwujący brak zaznaczenia w lewym menu</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>
|
<li>Znaczna ilość błędów z poprzednich wersji już nie występuje</li>
|
||||||
</ul>
|
</ul>-->
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
Dzięki za korzystanie ze Szkolnego!<br>
|
Dzięki za korzystanie ze Szkolnego!<br>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
/*secret password - removed for source code publication*/
|
/*secret password - removed for source code publication*/
|
||||||
static toys AES_IV[16] = {
|
static toys AES_IV[16] = {
|
||||||
0xa7, 0x84, 0xf9, 0xdc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
0x1c, 0x15, 0x0f, 0x1c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||||
|
|
||||||
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);
|
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);
|
||||||
|
|
||||||
|
@ -40,6 +40,9 @@ import androidx.core.util.forEach
|
|||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
import androidx.viewpager.widget.ViewPager
|
||||||
import com.google.android.gms.security.ProviderInstaller
|
import com.google.android.gms.security.ProviderInstaller
|
||||||
import com.google.gson.JsonArray
|
import com.google.gson.JsonArray
|
||||||
import com.google.gson.JsonElement
|
import com.google.gson.JsonElement
|
||||||
@ -141,6 +144,10 @@ fun CharSequence?.isNotNullNorEmpty(): Boolean {
|
|||||||
return this != null && this.isNotEmpty()
|
return this != null && this.isNotEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T> Collection<T>?.isNotNullNorEmpty(): Boolean {
|
||||||
|
return this != null && this.isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
fun CharSequence?.isNotNullNorBlank(): Boolean {
|
fun CharSequence?.isNotNullNorBlank(): Boolean {
|
||||||
return this != null && this.isNotBlank()
|
return this != null && this.isNotBlank()
|
||||||
}
|
}
|
||||||
@ -722,6 +729,13 @@ inline fun <T : View> T.onClick(crossinline onClickListener: (v: T) -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
inline fun <T : View> T.onLongClick(crossinline onLongClickListener: (v: T) -> Boolean) {
|
||||||
|
setOnLongClickListener { v: View ->
|
||||||
|
onLongClickListener(v as T)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
inline fun <T : CompoundButton> T.onChange(crossinline onChangeListener: (v: T, isChecked: Boolean) -> Unit) {
|
inline fun <T : CompoundButton> T.onChange(crossinline onChangeListener: (v: T, isChecked: Boolean) -> Unit) {
|
||||||
setOnCheckedChangeListener { buttonView, isChecked ->
|
setOnCheckedChangeListener { buttonView, isChecked ->
|
||||||
@ -1167,3 +1181,19 @@ fun TextView.getTextPosition(range: IntRange): Rect {
|
|||||||
|
|
||||||
return parentTextViewRect
|
return parentTextViewRect
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun ViewPager.addOnPageSelectedListener(crossinline block: (position: Int) -> Unit) = addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
|
||||||
|
override fun onPageScrollStateChanged(state: Int) {}
|
||||||
|
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
|
||||||
|
override fun onPageSelected(position: Int) { block(position) }
|
||||||
|
})
|
||||||
|
|
||||||
|
val SwipeRefreshLayout.onScrollListener: RecyclerView.OnScrollListener
|
||||||
|
get() = object : RecyclerView.OnScrollListener() {
|
||||||
|
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||||
|
if (recyclerView.canScrollVertically(-1))
|
||||||
|
this@onScrollListener.isEnabled = false
|
||||||
|
if (!recyclerView.canScrollVertically(-1) && newState == RecyclerView.SCROLL_STATE_IDLE)
|
||||||
|
this@onScrollListener.isEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -61,7 +61,7 @@ import pl.szczodrzynski.edziennik.ui.modules.behaviour.BehaviourFragment
|
|||||||
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar
|
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackFragment
|
import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.feedback.HelpFragment
|
import pl.szczodrzynski.edziennik.ui.modules.feedback.HelpFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesFragment
|
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesListFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.grades.editor.GradesEditorFragment
|
import pl.szczodrzynski.edziennik.ui.modules.grades.editor.GradesEditorFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
|
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment
|
import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment
|
||||||
@ -70,9 +70,10 @@ import pl.szczodrzynski.edziennik.ui.modules.messages.MessageFragment
|
|||||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeFragment
|
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
|
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesListFragment
|
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesListFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsFragment
|
import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsListFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment
|
import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsNewFragment
|
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsNewFragment
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.template.TemplateFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
|
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.webpush.WebPushFragment
|
import pl.szczodrzynski.edziennik.ui.modules.webpush.WebPushFragment
|
||||||
import pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoTouch
|
import pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoTouch
|
||||||
@ -129,6 +130,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
const val TARGET_MESSAGES_DETAILS = 503
|
const val TARGET_MESSAGES_DETAILS = 503
|
||||||
const val TARGET_MESSAGES_COMPOSE = 504
|
const val TARGET_MESSAGES_COMPOSE = 504
|
||||||
const val TARGET_WEB_PUSH = 140
|
const val TARGET_WEB_PUSH = 140
|
||||||
|
const val TARGET_TEMPLATE = 1000
|
||||||
|
|
||||||
const val HOME_ID = DRAWER_ITEM_HOME
|
const val HOME_ID = DRAWER_ITEM_HOME
|
||||||
|
|
||||||
@ -153,7 +155,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
.withBadgeTypeId(TYPE_EVENT)
|
.withBadgeTypeId(TYPE_EVENT)
|
||||||
.isInDrawer(true)
|
.isInDrawer(true)
|
||||||
|
|
||||||
list += NavTarget(DRAWER_ITEM_GRADES, R.string.menu_grades, GradesFragment::class)
|
list += NavTarget(DRAWER_ITEM_GRADES, R.string.menu_grades, GradesListFragment::class)
|
||||||
.withIcon(CommunityMaterial.Icon2.cmd_numeric_5_box_outline)
|
.withIcon(CommunityMaterial.Icon2.cmd_numeric_5_box_outline)
|
||||||
.withBadgeTypeId(TYPE_GRADE)
|
.withBadgeTypeId(TYPE_GRADE)
|
||||||
.isInDrawer(true)
|
.isInDrawer(true)
|
||||||
@ -185,7 +187,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
|
|
||||||
|
|
||||||
// static drawer items
|
// static drawer items
|
||||||
list += NavTarget(DRAWER_ITEM_NOTIFICATIONS, R.string.menu_notifications, NotificationsFragment::class)
|
list += NavTarget(DRAWER_ITEM_NOTIFICATIONS, R.string.menu_notifications, NotificationsListFragment::class)
|
||||||
.withIcon(CommunityMaterial.Icon.cmd_bell_ring_outline)
|
.withIcon(CommunityMaterial.Icon.cmd_bell_ring_outline)
|
||||||
.isInDrawer(true)
|
.isInDrawer(true)
|
||||||
.isStatic(true)
|
.isStatic(true)
|
||||||
@ -227,6 +229,13 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
list += NavTarget(TARGET_MESSAGES_COMPOSE, R.string.menu_message_compose, MessagesComposeFragment::class)
|
list += NavTarget(TARGET_MESSAGES_COMPOSE, R.string.menu_message_compose, MessagesComposeFragment::class)
|
||||||
list += NavTarget(TARGET_WEB_PUSH, R.string.menu_web_push, WebPushFragment::class)
|
list += NavTarget(TARGET_WEB_PUSH, R.string.menu_web_push, WebPushFragment::class)
|
||||||
list += NavTarget(DRAWER_ITEM_DEBUG, R.string.menu_debug, DebugFragment::class)
|
list += NavTarget(DRAWER_ITEM_DEBUG, R.string.menu_debug, DebugFragment::class)
|
||||||
|
if (App.devMode) {
|
||||||
|
list += NavTarget(TARGET_TEMPLATE, R.string.menu_template, TemplateFragment::class)
|
||||||
|
.withIcon(CommunityMaterial.Icon2.cmd_test_tube_empty)
|
||||||
|
.isInDrawer(true)
|
||||||
|
.isBelowSeparator(true)
|
||||||
|
.isStatic(true)
|
||||||
|
}
|
||||||
|
|
||||||
list
|
list
|
||||||
}
|
}
|
||||||
@ -1068,6 +1077,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
.also { if (target.icon != null) it.withIcon(target.icon!!) }
|
.also { if (target.icon != null) it.withIcon(target.icon!!) }
|
||||||
.also { if (target.title != null) it.withAppTitle(getString(target.title!!)) }
|
.also { if (target.title != null) it.withAppTitle(getString(target.title!!)) }
|
||||||
.also { if (target.badgeTypeId != null) it.withBadgeStyle(drawer.badgeStyle)}
|
.also { if (target.badgeTypeId != null) it.withBadgeStyle(drawer.badgeStyle)}
|
||||||
|
.withSelectedBackgroundAnimated(false)
|
||||||
|
|
||||||
if (target.badgeTypeId != null)
|
if (target.badgeTypeId != null)
|
||||||
drawer.addUnreadCounterType(target.badgeTypeId!!, target.id)
|
drawer.addUnreadCounterType(target.badgeTypeId!!, target.id)
|
||||||
|
@ -22,7 +22,7 @@ import kotlin.coroutines.CoroutineContext
|
|||||||
|
|
||||||
class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
|
class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
|
||||||
companion object {
|
companion object {
|
||||||
const val DATA_VERSION = 11
|
const val DATA_VERSION = 12
|
||||||
}
|
}
|
||||||
|
|
||||||
private val job = Job()
|
private val job = Job()
|
||||||
|
@ -18,7 +18,7 @@ import kotlin.coroutines.CoroutineContext
|
|||||||
|
|
||||||
class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEntry>) : CoroutineScope, AbstractConfig {
|
class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEntry>) : CoroutineScope, AbstractConfig {
|
||||||
companion object {
|
companion object {
|
||||||
const val DATA_VERSION = 1
|
const val DATA_VERSION = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
private val job = Job()
|
private val job = Job()
|
||||||
|
@ -64,11 +64,25 @@ class ConfigMigration(app: App, config: Config) {
|
|||||||
dataVersion = 2
|
dataVersion = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dataVersion < 3) {
|
||||||
|
update = null
|
||||||
|
privacyPolicyAccepted = false
|
||||||
|
debugMode = false
|
||||||
|
devModePassword = null
|
||||||
|
appInstalledTime = 0L
|
||||||
|
appRateSnackbarTime = 0L
|
||||||
|
|
||||||
|
dataVersion = 3
|
||||||
|
}
|
||||||
|
|
||||||
if (dataVersion < 10) {
|
if (dataVersion < 10) {
|
||||||
ui.openDrawerOnBackPressed = false
|
ui.openDrawerOnBackPressed = false
|
||||||
ui.snowfall = false
|
ui.snowfall = false
|
||||||
ui.bottomSheetOpened = false
|
ui.bottomSheetOpened = false
|
||||||
sync.dontShowAppManagerDialog = false
|
sync.dontShowAppManagerDialog = false
|
||||||
|
sync.webPushEnabled = true
|
||||||
|
sync.lastAppSync = 0L
|
||||||
|
|
||||||
|
|
||||||
dataVersion = 10
|
dataVersion = 10
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
package pl.szczodrzynski.edziennik.config.utils
|
package pl.szczodrzynski.edziennik.config.utils
|
||||||
|
|
||||||
import pl.szczodrzynski.edziennik.config.ProfileConfig
|
import pl.szczodrzynski.edziennik.config.ProfileConfig
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT
|
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT
|
||||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_WEIGHTED
|
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_WEIGHTED
|
||||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES
|
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES
|
||||||
@ -14,11 +15,23 @@ class ProfileConfigMigration(config: ProfileConfig) {
|
|||||||
|
|
||||||
if (dataVersion < 1) {
|
if (dataVersion < 1) {
|
||||||
grades.colorMode = COLOR_MODE_WEIGHTED
|
grades.colorMode = COLOR_MODE_WEIGHTED
|
||||||
grades.dontCountEnabled = false
|
|
||||||
grades.yearAverageMode = YEAR_ALL_GRADES
|
grades.yearAverageMode = YEAR_ALL_GRADES
|
||||||
|
grades.hideImproved = false
|
||||||
|
grades.averageWithoutWeight = true
|
||||||
|
grades.plusValue = null
|
||||||
|
grades.minusValue = null
|
||||||
|
grades.dontCountEnabled = false
|
||||||
|
grades.dontCountGrades = listOf()
|
||||||
ui.agendaViewType = AGENDA_DEFAULT
|
ui.agendaViewType = AGENDA_DEFAULT
|
||||||
|
// no migration for ui.homeCards
|
||||||
|
|
||||||
dataVersion = 1
|
dataVersion = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dataVersion < 2) {
|
||||||
|
sync.notificationFilter = sync.notificationFilter + Notification.TYPE_TEACHER_ABSENCE
|
||||||
|
|
||||||
|
dataVersion = 2
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ class LibrusMessagesGetAttachment(override val data: DataLibrus,
|
|||||||
downloadAttachment("${LIBRUS_SANDBOX_URL}CSDownload&singleUseKey=$attachmentKey", method = POST)
|
downloadAttachment("${LIBRUS_SANDBOX_URL}CSDownload&singleUseKey=$attachmentKey", method = POST)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
downloadAttachment(downloadLink, method = GET)
|
downloadAttachment("$downloadLink/get", method = GET)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,8 @@ class MobidziennikApiEvents(val data: DataMobidziennik, rows: List<String>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data.toRemove.add(DataRemoveModel.Events.futureExceptType(Event.TYPE_HOMEWORK))
|
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_DEFAULT))
|
||||||
|
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_EXAM))
|
||||||
|
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_SHORT_QUIZ))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -284,7 +284,7 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
|
|||||||
db.gradeDao().addAll(gradeList)
|
db.gradeDao().addAll(gradeList)
|
||||||
}
|
}
|
||||||
if (eventList.isNotEmpty()) {
|
if (eventList.isNotEmpty()) {
|
||||||
db.eventDao().addAll(eventList)
|
db.eventDao().upsertAll(eventList, removeNotKept = true)
|
||||||
}
|
}
|
||||||
if (noticeList.isNotEmpty()) {
|
if (noticeList.isNotEmpty()) {
|
||||||
db.noticeDao().clear(profile.id)
|
db.noticeDao().clear(profile.id)
|
||||||
|
@ -56,9 +56,9 @@ open class DataRemoveModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun commit(profileId: Int, dao: EventDao) {
|
fun commit(profileId: Int, dao: EventDao) {
|
||||||
type?.let { dao.removeFutureWithType(profileId, Date.getToday(), it) }
|
type?.let { dao.dontKeepFutureWithType(profileId, Date.getToday(), it) }
|
||||||
exceptType?.let { dao.removeFutureExceptType(profileId, Date.getToday(), it) }
|
exceptType?.let { dao.dontKeepFutureExceptType(profileId, Date.getToday(), it) }
|
||||||
exceptTypes?.let { dao.removeFutureExceptTypes(profileId, Date.getToday(), it) }
|
exceptTypes?.let { dao.dontKeepFutureExceptTypes(profileId, Date.getToday(), it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +46,6 @@ object Signing {
|
|||||||
|
|
||||||
/*fun provideKey(param1: String, param2: Long): ByteArray {*/
|
/*fun provideKey(param1: String, param2: Long): ByteArray {*/
|
||||||
fun pleaseStopRightNow(param1: String, param2: Long): ByteArray {
|
fun pleaseStopRightNow(param1: String, param2: Long): ByteArray {
|
||||||
return "$param1.MTIzNDU2Nzg5MDZhgrg9J9===.$param2".sha256()
|
return "$param1.MTIzNDU2Nzg5MDj3yyZoD8===.$param2".sha256()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ class AppSync(val app: App, val notifications: MutableList<Notification>, val pr
|
|||||||
event.addedDate
|
event.addedDate
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
return app.db.eventDao().addAll(events).size
|
return app.db.eventDao().upsertAll(events).size
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ import pl.szczodrzynski.edziennik.data.db.migration.*
|
|||||||
LibrusLesson::class,
|
LibrusLesson::class,
|
||||||
TimetableManual::class,
|
TimetableManual::class,
|
||||||
Metadata::class
|
Metadata::class
|
||||||
], version = 81)
|
], version = 83)
|
||||||
@TypeConverters(
|
@TypeConverters(
|
||||||
ConverterTime::class,
|
ConverterTime::class,
|
||||||
ConverterDate::class,
|
ConverterDate::class,
|
||||||
@ -166,7 +166,9 @@ abstract class AppDb : RoomDatabase() {
|
|||||||
Migration78(),
|
Migration78(),
|
||||||
Migration79(),
|
Migration79(),
|
||||||
Migration80(),
|
Migration80(),
|
||||||
Migration81()
|
Migration81(),
|
||||||
|
Migration82(),
|
||||||
|
Migration83()
|
||||||
).allowMainThreadQueries().build()
|
).allowMainThreadQueries().build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,15 +5,13 @@
|
|||||||
package pl.szczodrzynski.edziennik.data.db.dao
|
package pl.szczodrzynski.edziennik.data.db.dao
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.room.Dao
|
import androidx.room.*
|
||||||
import androidx.room.Insert
|
|
||||||
import androidx.room.OnConflictStrategy
|
|
||||||
import androidx.room.RawQuery
|
|
||||||
import androidx.sqlite.db.SimpleSQLiteQuery
|
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||||
import androidx.sqlite.db.SupportSQLiteQuery
|
import androidx.sqlite.db.SupportSQLiteQuery
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.entity.Keepable
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface BaseDao<T, F> {
|
interface BaseDao<T : Keepable, F : T> {
|
||||||
@RawQuery
|
@RawQuery
|
||||||
fun getRaw(query: SupportSQLiteQuery): LiveData<List<F>>
|
fun getRaw(query: SupportSQLiteQuery): LiveData<List<F>>
|
||||||
fun getRaw(query: String) = getRaw(SimpleSQLiteQuery(query))
|
fun getRaw(query: String) = getRaw(SimpleSQLiteQuery(query))
|
||||||
@ -24,11 +22,79 @@ interface BaseDao<T, F> {
|
|||||||
fun getOneNow(query: SupportSQLiteQuery): F?
|
fun getOneNow(query: SupportSQLiteQuery): F?
|
||||||
fun getOneNow(query: String) = getOneNow(SimpleSQLiteQuery(query))
|
fun getOneNow(query: String) = getOneNow(SimpleSQLiteQuery(query))
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Query("DELETE FROM events WHERE keep = 0")
|
||||||
fun add(item: T): Long
|
fun removeNotKept()
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
/**
|
||||||
|
* INSERT an [item] into the database,
|
||||||
|
* ignoring any conflicts.
|
||||||
|
*/
|
||||||
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
|
fun add(item: T): Long
|
||||||
|
/**
|
||||||
|
* INSERT [items] into the database,
|
||||||
|
* ignoring any conflicts.
|
||||||
|
*/
|
||||||
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
fun addAll(items: List<T>): LongArray
|
fun addAll(items: List<T>): LongArray
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REPLACE an [item] in the database,
|
||||||
|
* removing any conflicting rows.
|
||||||
|
*/
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun replace(item: T)
|
||||||
|
/**
|
||||||
|
* REPLACE [items] in the database,
|
||||||
|
* removing any conflicting rows.
|
||||||
|
*/
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun replaceAll(items: List<T>)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selective UPDATE an [item] in the database.
|
||||||
|
* Do nothing if a matching item does not exist.
|
||||||
|
*/
|
||||||
|
fun update(item: T): Long
|
||||||
|
/**
|
||||||
|
* Selective UPDATE [items] in the database.
|
||||||
|
* Do nothing for those items which do not exist.
|
||||||
|
*/
|
||||||
|
fun updateAll(items: List<T>): LongArray
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all items from the database,
|
||||||
|
* that match the given [profileId].
|
||||||
|
*/
|
||||||
fun clear(profileId: Int)
|
fun clear(profileId: Int)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* INSERT an [item] into the database,
|
||||||
|
* doing a selective [update] on conflicts.
|
||||||
|
* @return the newly inserted item's ID or -1L if the item was updated instead
|
||||||
|
*/
|
||||||
|
@Transaction
|
||||||
|
fun upsert(item: T): Long {
|
||||||
|
val id = add(item)
|
||||||
|
if (id == -1L) update(item)
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* INSERT [items] into the database,
|
||||||
|
* doing a selective [update] on conflicts.
|
||||||
|
* @return a [LongArray] of IDs of newly inserted items or -1L if the item existed before
|
||||||
|
*/
|
||||||
|
@Transaction
|
||||||
|
fun upsertAll(items: List<T>, removeNotKept: Boolean = false): LongArray {
|
||||||
|
val insertResult = addAll(items)
|
||||||
|
val updateList = mutableListOf<T>()
|
||||||
|
|
||||||
|
insertResult.forEachIndexed { index, result ->
|
||||||
|
if (result == -1L) updateList.add(items[index])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateList.isNotEmpty()) updateAll(items)
|
||||||
|
if (removeNotKept) removeNotKept()
|
||||||
|
return insertResult
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,10 @@ import androidx.room.RawQuery
|
|||||||
import androidx.room.Transaction
|
import androidx.room.Transaction
|
||||||
import androidx.sqlite.db.SimpleSQLiteQuery
|
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||||
import androidx.sqlite.db.SupportSQLiteQuery
|
import androidx.sqlite.db.SupportSQLiteQuery
|
||||||
|
import pl.szczodrzynski.edziennik.App
|
||||||
|
import pl.szczodrzynski.edziennik.annotation.SelectiveDao
|
||||||
|
import pl.szczodrzynski.edziennik.annotation.UpdateSelective
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.AppDb
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||||
@ -17,6 +21,7 @@ import pl.szczodrzynski.edziennik.utils.models.Date
|
|||||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
|
@SelectiveDao(db = AppDb::class)
|
||||||
abstract class EventDao : BaseDao<Event, EventFull> {
|
abstract class EventDao : BaseDao<Event, EventFull> {
|
||||||
companion object {
|
companion object {
|
||||||
private const val QUERY = """
|
private const val QUERY = """
|
||||||
@ -35,19 +40,91 @@ abstract class EventDao : BaseDao<Event, EventFull> {
|
|||||||
|
|
||||||
private const val ORDER_BY = """GROUP BY eventId ORDER BY eventDate, eventTime, addedDate ASC"""
|
private const val ORDER_BY = """GROUP BY eventId ORDER BY eventDate, eventTime, addedDate ASC"""
|
||||||
private const val NOT_BLACKLISTED = """events.eventBlacklisted = 0"""
|
private const val NOT_BLACKLISTED = """events.eventBlacklisted = 0"""
|
||||||
|
private const val NOT_DONE = """events.eventIsDone = 0"""
|
||||||
}
|
}
|
||||||
|
|
||||||
//abstract fun queryRaw(query: SupportSQLiteQuery)
|
private val selective by lazy { EventDaoSelective(App.db) }
|
||||||
//private fun queryRaw(query: String) = queryRaw(SimpleSQLiteQuery(query))
|
|
||||||
|
|
||||||
|
@RawQuery(observedEntities = [Event::class])
|
||||||
|
abstract override fun getRaw(query: SupportSQLiteQuery): LiveData<List<EventFull>>
|
||||||
|
|
||||||
|
// SELECTIVE UPDATE
|
||||||
|
@UpdateSelective(primaryKeys = ["profileId", "eventId"], skippedColumns = ["eventIsDone", "eventBlacklisted", "homeworkBody", "attachmentIds", "attachmentNames"])
|
||||||
|
override fun update(item: Event) = selective.update(item)
|
||||||
|
override fun updateAll(items: List<Event>) = selective.updateAll(items)
|
||||||
|
|
||||||
|
// CLEAR
|
||||||
@Query("DELETE FROM events WHERE profileId = :profileId")
|
@Query("DELETE FROM events WHERE profileId = :profileId")
|
||||||
abstract override fun clear(profileId: Int)
|
abstract override fun clear(profileId: Int)
|
||||||
|
|
||||||
/*fun update(event: Event) =
|
// GET ALL - LIVE DATA
|
||||||
queryRaw("""UPDATE events SET
|
fun getAll(profileId: Int) =
|
||||||
eventDate = '${event.date.stringY_m_d}',
|
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId $ORDER_BY")
|
||||||
eventTime = ${event.time?.stringValue},
|
fun getAllByType(profileId: Int, type: Long, filter: String = "1") =
|
||||||
eventTopic = '${event.topic}'""")*/
|
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventType = $type AND $filter $ORDER_BY")
|
||||||
|
fun getAllByDate(profileId: Int, date: Date) =
|
||||||
|
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate = '${date.stringY_m_d}' $ORDER_BY")
|
||||||
|
fun getAllByDateTime(profileId: Int, date: Date, time: Time) =
|
||||||
|
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate = '${date.stringY_m_d}' AND eventTime = '${time.stringValue}'")
|
||||||
|
fun getNearestNotDone(profileId: Int, today: Date, limit: Int) =
|
||||||
|
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND $NOT_DONE AND events.profileId = $profileId AND eventDate >= '${today.stringY_m_d}' $ORDER_BY LIMIT $limit")
|
||||||
|
|
||||||
|
// GET ALL - NOW
|
||||||
|
fun getAllNow(profileId: Int) =
|
||||||
|
getRawNow("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId $ORDER_BY")
|
||||||
|
fun getNotNotifiedNow() =
|
||||||
|
getRawNow("$QUERY WHERE $NOT_BLACKLISTED AND notified = 0 $ORDER_BY")
|
||||||
|
fun getNotNotifiedNow(profileId: Int) =
|
||||||
|
getRawNow("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND notified = 0 $ORDER_BY")
|
||||||
|
fun getAllByDateNow(profileId: Int, date: Date) =
|
||||||
|
getRawNow("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate = '${date.stringY_m_d}' $ORDER_BY")
|
||||||
|
|
||||||
|
// GET ONE - NOW
|
||||||
|
fun getByIdNow(profileId: Int, id: Long) =
|
||||||
|
getOneNow("$QUERY WHERE events.profileId = $profileId AND eventId = $id")
|
||||||
|
|
||||||
|
|
||||||
|
@Query("SELECT eventId FROM events WHERE profileId = :profileId AND eventBlacklisted = 1")
|
||||||
|
abstract fun getBlacklistedIds(profileId: Int): List<Long>
|
||||||
|
|
||||||
|
@get:Query("SELECT eventId FROM events WHERE eventBlacklisted = 1")
|
||||||
|
abstract val blacklistedIds: List<Long>
|
||||||
|
|
||||||
|
/*@Query("UPDATE events SET eventAddedManually = 1 WHERE profileId = :profileId AND eventDate < :date")
|
||||||
|
abstract fun convertOlderToManual(profileId: Int, date: Date?)
|
||||||
|
|
||||||
|
@Query("DELETE FROM events WHERE teamId = :teamId AND eventId = :id")
|
||||||
|
abstract fun removeByTeamId(teamId: Long, id: Long)
|
||||||
|
|
||||||
|
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0")
|
||||||
|
abstract fun removeNotManual(profileId: Int)*/
|
||||||
|
|
||||||
|
@RawQuery
|
||||||
|
abstract fun dontKeepFuture(query: SupportSQLiteQuery?): Long
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
open fun dontKeepFuture(profileId: Int, todayDate: Date, filter: String) {
|
||||||
|
dontKeepFuture(SimpleSQLiteQuery("UPDATE events SET keep = 0 WHERE profileId = " + profileId
|
||||||
|
+ " AND eventAddedManually = 0 AND eventDate >= '" + todayDate.stringY_m_d + "'" +
|
||||||
|
" AND " + filter))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Query("UPDATE events SET keep = 0 WHERE profileId = :profileId AND eventAddedManually = 0 AND eventDate >= :todayDate AND eventType = :type")
|
||||||
|
abstract fun dontKeepFutureWithType(profileId: Int, todayDate: Date, type: Long)
|
||||||
|
|
||||||
|
@Query("UPDATE events SET keep = 0 WHERE profileId = :profileId AND eventAddedManually = 0 AND eventDate >= :todayDate AND eventType != :exceptType")
|
||||||
|
abstract fun dontKeepFutureExceptType(profileId: Int, todayDate: Date, exceptType: Long)
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
open fun dontKeepFutureExceptTypes(profileId: Int, todayDate: Date, exceptTypes: List<Long>) {
|
||||||
|
dontKeepFuture(profileId, todayDate, "eventType NOT IN " + exceptTypes.toString().replace('[', '(').replace(']', ')'))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Query("UPDATE metadata SET seen = :seen WHERE profileId = :profileId AND (thingType = " + Metadata.TYPE_EVENT + " OR thingType = " + Metadata.TYPE_LESSON_CHANGE + " OR thingType = " + Metadata.TYPE_HOMEWORK + ") AND thingId IN (SELECT eventId FROM events WHERE profileId = :profileId AND eventDate = :date)")
|
||||||
|
abstract fun setSeenByDate(profileId: Int, date: Date, seen: Boolean)
|
||||||
|
|
||||||
|
@Query("UPDATE events SET eventBlacklisted = :blacklisted WHERE profileId = :profileId AND eventId = :eventId")
|
||||||
|
abstract fun setBlacklisted(profileId: Int, eventId: Long, blacklisted: Boolean)
|
||||||
|
|
||||||
@Query("DELETE FROM events WHERE profileId = :profileId AND eventId = :id")
|
@Query("DELETE FROM events WHERE profileId = :profileId AND eventId = :id")
|
||||||
abstract fun remove(profileId: Int, id: Long)
|
abstract fun remove(profileId: Int, id: Long)
|
||||||
@ -71,80 +148,4 @@ abstract class EventDao : BaseDao<Event, EventFull> {
|
|||||||
remove(profileId, event.type, event.id)
|
remove(profileId, event.type, event.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@RawQuery(observedEntities = [Event::class])
|
|
||||||
abstract override fun getRaw(query: SupportSQLiteQuery): LiveData<List<EventFull>>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun getAll(profileId: Int) =
|
|
||||||
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId $ORDER_BY")
|
|
||||||
fun getAllByType(profileId: Int, type: Long, filter: String = "1") =
|
|
||||||
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventType = $type AND $filter $ORDER_BY")
|
|
||||||
fun getAllByDate(profileId: Int, date: Date) =
|
|
||||||
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate = '${date.stringY_m_d}' $ORDER_BY")
|
|
||||||
fun getAllByDateTime(profileId: Int, date: Date, time: Time) =
|
|
||||||
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate = '${date.stringY_m_d}' AND eventTime = ${time.stringValue}")
|
|
||||||
fun getAllNearest(profileId: Int, today: Date, limit: Int) =
|
|
||||||
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate >= '${today.stringY_m_d}' $ORDER_BY LIMIT $limit")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun getAllNow(profileId: Int) =
|
|
||||||
getRawNow("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId $ORDER_BY")
|
|
||||||
fun getNotNotifiedNow() =
|
|
||||||
getRawNow("$QUERY WHERE $NOT_BLACKLISTED AND notified = 0 $ORDER_BY")
|
|
||||||
fun getNotNotifiedNow(profileId: Int) =
|
|
||||||
getRawNow("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND notified = 0 $ORDER_BY")
|
|
||||||
fun getAllByDateNow(profileId: Int, date: Date) =
|
|
||||||
getRawNow("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate = '${date.stringY_m_d}' $ORDER_BY")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun getByIdNow(profileId: Int, id: Long) =
|
|
||||||
getOneNow("$QUERY WHERE events.profileId = $profileId AND eventId = $id")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Query("SELECT eventId FROM events WHERE profileId = :profileId AND eventBlacklisted = 1")
|
|
||||||
abstract fun getBlacklistedIds(profileId: Int): List<Long>
|
|
||||||
|
|
||||||
@get:Query("SELECT eventId FROM events WHERE eventBlacklisted = 1")
|
|
||||||
abstract val blacklistedIds: List<Long>
|
|
||||||
|
|
||||||
@Query("UPDATE events SET eventAddedManually = 1 WHERE profileId = :profileId AND eventDate < :date")
|
|
||||||
abstract fun convertOlderToManual(profileId: Int, date: Date?)
|
|
||||||
|
|
||||||
@Query("DELETE FROM events WHERE teamId = :teamId AND eventId = :id")
|
|
||||||
abstract fun removeByTeamId(teamId: Long, id: Long)
|
|
||||||
|
|
||||||
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0")
|
|
||||||
abstract fun removeNotManual(profileId: Int)
|
|
||||||
|
|
||||||
@RawQuery
|
|
||||||
abstract fun removeFuture(query: SupportSQLiteQuery?): Long
|
|
||||||
|
|
||||||
@Transaction
|
|
||||||
open fun removeFuture(profileId: Int, todayDate: Date, filter: String) {
|
|
||||||
removeFuture(SimpleSQLiteQuery("DELETE FROM events WHERE profileId = " + profileId
|
|
||||||
+ " AND eventAddedManually = 0 AND eventDate >= '" + todayDate.stringY_m_d + "'" +
|
|
||||||
" AND " + filter))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0 AND eventDate >= :todayDate AND eventType = :type")
|
|
||||||
abstract fun removeFutureWithType(profileId: Int, todayDate: Date, type: Long)
|
|
||||||
|
|
||||||
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0 AND eventDate >= :todayDate AND eventType != :exceptType")
|
|
||||||
abstract fun removeFutureExceptType(profileId: Int, todayDate: Date, exceptType: Long)
|
|
||||||
|
|
||||||
@Transaction
|
|
||||||
open fun removeFutureExceptTypes(profileId: Int, todayDate: Date, exceptTypes: List<Long>) {
|
|
||||||
removeFuture(profileId, todayDate, "eventType NOT IN " + exceptTypes.toString().replace('[', '(').replace(']', ')'))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Query("UPDATE metadata SET seen = :seen WHERE profileId = :profileId AND (thingType = " + Metadata.TYPE_EVENT + " OR thingType = " + Metadata.TYPE_LESSON_CHANGE + " OR thingType = " + Metadata.TYPE_HOMEWORK + ") AND thingId IN (SELECT eventId FROM events WHERE profileId = :profileId AND eventDate = :date)")
|
|
||||||
abstract fun setSeenByDate(profileId: Int, date: Date, seen: Boolean)
|
|
||||||
|
|
||||||
@Query("UPDATE events SET eventBlacklisted = :blacklisted WHERE profileId = :profileId AND eventId = :eventId")
|
|
||||||
abstract fun setBlacklisted(profileId: Int, eventId: Long, blacklisted: Boolean)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ open class Event(
|
|||||||
var teacherId: Long,
|
var teacherId: Long,
|
||||||
var subjectId: Long,
|
var subjectId: Long,
|
||||||
var teamId: Long
|
var teamId: Long
|
||||||
) {
|
) : Keepable() {
|
||||||
companion object {
|
companion object {
|
||||||
const val TYPE_UNDEFINED = -2L
|
const val TYPE_UNDEFINED = -2L
|
||||||
const val TYPE_HOMEWORK = -1L
|
const val TYPE_HOMEWORK = -1L
|
||||||
@ -79,6 +79,8 @@ open class Event(
|
|||||||
var sharedByName: String? = null
|
var sharedByName: String? = null
|
||||||
@ColumnInfo(name = "eventBlacklisted")
|
@ColumnInfo(name = "eventBlacklisted")
|
||||||
var blacklisted: Boolean = false
|
var blacklisted: Boolean = false
|
||||||
|
@ColumnInfo(name = "eventIsDone")
|
||||||
|
var isDone: Boolean = false
|
||||||
|
|
||||||
var homeworkBody: String? = null
|
var homeworkBody: String? = null
|
||||||
var attachmentIds: List<Long>? = null
|
var attachmentIds: List<Long>? = null
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-3-30.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.data.db.entity
|
||||||
|
|
||||||
|
abstract class Keepable {
|
||||||
|
var keep: Boolean = true
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package pl.szczodrzynski.edziennik.data.db.migration
|
||||||
|
|
||||||
|
import androidx.room.migration.Migration
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
|
||||||
|
class Migration82 : Migration(81, 82) {
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("ALTER TABLE events ADD COLUMN eventIsDone INTEGER DEFAULT 0 NOT NULL")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-3-30.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.data.db.migration
|
||||||
|
|
||||||
|
import androidx.room.migration.Migration
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
|
||||||
|
class Migration83 : Migration(82, 83) {
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("ALTER TABLE events ADD COLUMN keep INTEGER NOT NULL DEFAULT 1")
|
||||||
|
}
|
||||||
|
}
|
@ -152,7 +152,7 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
|
|||||||
events += event
|
events += event
|
||||||
metadataList += metadata
|
metadataList += metadata
|
||||||
}
|
}
|
||||||
app.db.eventDao().addAll(events)
|
app.db.eventDao().upsertAll(events)
|
||||||
app.db.metadataDao().addAllReplace(metadataList)
|
app.db.metadataDao().addAllReplace(metadataList)
|
||||||
if (notificationList.isNotEmpty()) {
|
if (notificationList.isNotEmpty()) {
|
||||||
app.db.notificationDao().addAll(notificationList)
|
app.db.notificationDao().addAll(notificationList)
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Kuba Szczodrzyński 2019-12-19.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package pl.szczodrzynski.edziennik.ui
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import pl.szczodrzynski.edziennik.App
|
|
||||||
import pl.szczodrzynski.edziennik.databinding.TemplateListItemBinding
|
|
||||||
import pl.szczodrzynski.edziennik.onClick
|
|
||||||
|
|
||||||
class TemplateAdapter(
|
|
||||||
val context: Context,
|
|
||||||
val onItemClick: ((item: TemplateItem) -> Unit)? = null,
|
|
||||||
val onItemButtonClick: ((item: TemplateItem) -> Unit)? = null
|
|
||||||
) : RecyclerView.Adapter<TemplateAdapter.ViewHolder>() {
|
|
||||||
|
|
||||||
private val app by lazy { context.applicationContext as App }
|
|
||||||
|
|
||||||
var items = listOf<TemplateItem>()
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
|
||||||
val inflater = LayoutInflater.from(parent.context)
|
|
||||||
val view = TemplateListItemBinding.inflate(inflater, parent, false)
|
|
||||||
return ViewHolder(view)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
|
||||||
val item = items[position]
|
|
||||||
val b = holder.b
|
|
||||||
|
|
||||||
onItemClick?.let { listener ->
|
|
||||||
b.root.onClick { listener(item) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/*b.someButton.visibility = if (buttonVisible) View.VISIBLE else View.GONE
|
|
||||||
onItemButtonClick?.let { listener ->
|
|
||||||
b.someButton.onClick { listener(item) }
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemCount() = items.size
|
|
||||||
|
|
||||||
class ViewHolder(val b: TemplateListItemBinding) : RecyclerView.ViewHolder(b.root)
|
|
||||||
|
|
||||||
data class TemplateItem(val text: String)
|
|
||||||
}
|
|
@ -146,6 +146,11 @@ class DayDialog(
|
|||||||
|
|
||||||
adapter = EventListAdapter(
|
adapter = EventListAdapter(
|
||||||
activity,
|
activity,
|
||||||
|
showWeekDay = false,
|
||||||
|
showDate = false,
|
||||||
|
showType = true,
|
||||||
|
showTime = true,
|
||||||
|
showSubject = true,
|
||||||
onItemClick = {
|
onItemClick = {
|
||||||
EventDetailsDialog(
|
EventDetailsDialog(
|
||||||
activity,
|
activity,
|
||||||
|
@ -148,6 +148,10 @@ class EventDetailsDialog(
|
|||||||
openInCalendar()
|
openInCalendar()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b.checkDoneButton.setOnLongClickListener {
|
||||||
|
Toast.makeText(activity, R.string.hint_mark_as_done, Toast.LENGTH_SHORT).show()
|
||||||
|
true
|
||||||
|
}
|
||||||
b.goToTimetableButton.setOnLongClickListener {
|
b.goToTimetableButton.setOnLongClickListener {
|
||||||
Toast.makeText(activity, R.string.hint_go_to_timetable, Toast.LENGTH_SHORT).show()
|
Toast.makeText(activity, R.string.hint_go_to_timetable, Toast.LENGTH_SHORT).show()
|
||||||
true
|
true
|
||||||
@ -161,6 +165,14 @@ class EventDetailsDialog(
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b.checkDoneButton.isChecked = event.isDone
|
||||||
|
b.checkDoneButton.addOnCheckedChangeListener { _, isChecked ->
|
||||||
|
event.isDone = isChecked
|
||||||
|
launch(Dispatchers.Default) {
|
||||||
|
app.db.eventDao().replace(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
b.topic.text = event.topic
|
b.topic.text = event.topic
|
||||||
BetterLink.attach(b.topic) {
|
BetterLink.attach(b.topic) {
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
|
@ -6,9 +6,9 @@ package pl.szczodrzynski.edziennik.ui.dialogs.event
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import pl.szczodrzynski.edziennik.*
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||||
@ -19,8 +19,11 @@ import pl.szczodrzynski.edziennik.utils.models.Week
|
|||||||
class EventListAdapter(
|
class EventListAdapter(
|
||||||
val context: Context,
|
val context: Context,
|
||||||
val simpleMode: Boolean = false,
|
val simpleMode: Boolean = false,
|
||||||
val showDate: Boolean = false,
|
|
||||||
val showWeekDay: Boolean = false,
|
val showWeekDay: Boolean = false,
|
||||||
|
val showDate: Boolean = false,
|
||||||
|
val showType: Boolean = true,
|
||||||
|
val showTime: Boolean = true,
|
||||||
|
val showSubject: Boolean = true,
|
||||||
val onItemClick: ((event: EventFull) -> Unit)? = null,
|
val onItemClick: ((event: EventFull) -> Unit)? = null,
|
||||||
val onEventEditClick: ((event: EventFull) -> Unit)? = null
|
val onEventEditClick: ((event: EventFull) -> Unit)? = null
|
||||||
) : RecyclerView.Adapter<EventListAdapter.ViewHolder>() {
|
) : RecyclerView.Adapter<EventListAdapter.ViewHolder>() {
|
||||||
@ -48,13 +51,14 @@ class EventListAdapter(
|
|||||||
b.simpleMode = simpleMode
|
b.simpleMode = simpleMode
|
||||||
|
|
||||||
b.topic.text = event.topic
|
b.topic.text = event.topic
|
||||||
|
b.topic.maxLines = if (simpleMode) 2 else 3
|
||||||
|
|
||||||
b.details.text = mutableListOf<CharSequence?>(
|
b.details.text = mutableListOf<CharSequence?>(
|
||||||
if (showWeekDay) Week.getFullDayName(event.date.weekDay) else null,
|
if (showWeekDay) Week.getFullDayName(event.date.weekDay) else null,
|
||||||
if (showDate) event.date.getRelativeString(context, 7) ?: event.date.formattedStringShort else null,
|
if (showDate) event.date.getRelativeString(context, 7) ?: event.date.formattedStringShort else null,
|
||||||
event.typeName,
|
if (showType) event.typeName else null,
|
||||||
if (simpleMode) null else event.time?.stringHM ?: app.getString(R.string.event_all_day),
|
if (showTime) event.time?.stringHM ?: app.getString(R.string.event_all_day) else null,
|
||||||
if (simpleMode) null else event.subjectLongName
|
if (showSubject) event.subjectLongName else null
|
||||||
).concat(bullet)
|
).concat(bullet)
|
||||||
|
|
||||||
b.addedBy.setText(
|
b.addedBy.setText(
|
||||||
@ -73,12 +77,15 @@ class EventListAdapter(
|
|||||||
)
|
)
|
||||||
|
|
||||||
b.typeColor.background?.setTintColor(event.eventColor)
|
b.typeColor.background?.setTintColor(event.eventColor)
|
||||||
|
b.typeColor.isVisible = showType
|
||||||
|
|
||||||
b.editButton.visibility = if (event.addedManually && !simpleMode) View.VISIBLE else View.GONE
|
b.editButton.isVisible = !simpleMode && event.addedManually && !event.isDone
|
||||||
b.editButton.onClick {
|
b.editButton.onClick {
|
||||||
onEventEditClick?.invoke(event)
|
onEventEditClick?.invoke(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b.isDone.isVisible = event.isDone
|
||||||
|
|
||||||
b.editButton.setOnLongClickListener {
|
b.editButton.setOnLongClickListener {
|
||||||
Toast.makeText(context, R.string.hint_edit_event, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, R.string.hint_edit_event, Toast.LENGTH_SHORT).show()
|
||||||
true
|
true
|
||||||
|
@ -587,7 +587,7 @@ class EventManualDialog(
|
|||||||
private fun finishAdding(eventObject: Event, metadataObject: Metadata) {
|
private fun finishAdding(eventObject: Event, metadataObject: Metadata) {
|
||||||
launch {
|
launch {
|
||||||
withContext(Dispatchers.Default) {
|
withContext(Dispatchers.Default) {
|
||||||
app.db.eventDao().add(eventObject)
|
app.db.eventDao().upsert(eventObject)
|
||||||
app.db.metadataDao().add(metadataObject)
|
app.db.metadataDao().add(metadataObject)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,6 +170,11 @@ class LessonDetailsDialog(
|
|||||||
|
|
||||||
adapter = EventListAdapter(
|
adapter = EventListAdapter(
|
||||||
activity,
|
activity,
|
||||||
|
showWeekDay = false,
|
||||||
|
showDate = false,
|
||||||
|
showType = true,
|
||||||
|
showTime = true,
|
||||||
|
showSubject = true,
|
||||||
onItemClick = {
|
onItemClick = {
|
||||||
EventDetailsDialog(
|
EventDetailsDialog(
|
||||||
activity,
|
activity,
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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()
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-3-30.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.modules.base.lazypager
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentManager
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
|
||||||
|
class FragmentLazyPagerAdapter(
|
||||||
|
fragmentManager: FragmentManager,
|
||||||
|
swipeRefreshLayout: SwipeRefreshLayout,
|
||||||
|
private val fragments: List<Pair<LazyFragment, CharSequence>>
|
||||||
|
) : LazyPagerAdapter(fragmentManager, swipeRefreshLayout) {
|
||||||
|
override fun getPage(position: Int) = fragments[position].first
|
||||||
|
override fun getPageTitle(position: Int) = fragments[position].second
|
||||||
|
override fun getCount() = fragments.size
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-3-29.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.modules.base.lazypager
|
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
|
||||||
|
abstract class LazyFragment : Fragment() {
|
||||||
|
private var isPageCreated = false
|
||||||
|
internal var position = -1
|
||||||
|
internal var swipeRefreshLayoutCallback: ((position: Int, isEnabled: Boolean) -> Unit)? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
|
||||||
|
fun enableSwipeToRefresh() = swipeRefreshLayoutCallback?.invoke(position, true)
|
||||||
|
fun disableSwipeToRefresh() = swipeRefreshLayoutCallback?.invoke(position, false)
|
||||||
|
fun setSwipeToRefresh(enabled: Boolean) = swipeRefreshLayoutCallback?.invoke(position, enabled)
|
||||||
|
|
||||||
|
val onScrollListener: RecyclerView.OnScrollListener
|
||||||
|
get() = object : RecyclerView.OnScrollListener() {
|
||||||
|
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||||
|
if (recyclerView.canScrollVertically(-1))
|
||||||
|
disableSwipeToRefresh()
|
||||||
|
if (!recyclerView.canScrollVertically(-1) && newState == RecyclerView.SCROLL_STATE_IDLE)
|
||||||
|
enableSwipeToRefresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun createPage() {
|
||||||
|
if (!isPageCreated && isAdded) {
|
||||||
|
isPageCreated = onPageCreated()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
isPageCreated = false
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
createPage()
|
||||||
|
super.onResume()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-3-29.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.modules.base.lazypager
|
||||||
|
|
||||||
|
import android.util.SparseBooleanArray
|
||||||
|
import androidx.core.util.set
|
||||||
|
import androidx.fragment.app.FragmentManager
|
||||||
|
import androidx.fragment.app.FragmentStatePagerAdapter
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
|
||||||
|
abstract class LazyPagerAdapter(fragmentManager: FragmentManager, val swipeRefreshLayout: SwipeRefreshLayout? = null) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
|
||||||
|
internal val enabledList = SparseBooleanArray()
|
||||||
|
private val refreshLayoutCallback: (position: Int, isEnabled: Boolean) -> Unit = { position, isEnabled ->
|
||||||
|
swipeRefreshLayout?.isEnabled = isEnabled
|
||||||
|
if (position > -1)
|
||||||
|
enabledList[position] = isEnabled
|
||||||
|
}
|
||||||
|
final override fun getItem(position: Int): LazyFragment {
|
||||||
|
return getPage(position).also {
|
||||||
|
it.position = position
|
||||||
|
it.swipeRefreshLayoutCallback = refreshLayoutCallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
abstract fun getPage(position: Int): LazyFragment
|
||||||
|
abstract override fun getPageTitle(position: Int): CharSequence
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-3-29.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.modules.base.lazypager
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import androidx.viewpager.widget.ViewPager
|
||||||
|
|
||||||
|
class LazyViewPager @JvmOverloads constructor(
|
||||||
|
context: Context, attrs: AttributeSet? = null
|
||||||
|
) : ViewPager(context, attrs) {
|
||||||
|
|
||||||
|
private var pageSelection = -1
|
||||||
|
private var scrollState = 0
|
||||||
|
|
||||||
|
init {
|
||||||
|
addOnPageChangeListener(object : OnPageChangeListener {
|
||||||
|
override fun onPageScrollStateChanged(state: Int) {
|
||||||
|
scrollState = state
|
||||||
|
(adapter as? LazyPagerAdapter)?.let {
|
||||||
|
it.swipeRefreshLayout?.isEnabled = state == SCROLL_STATE_IDLE && it.enabledList[pageSelection, true]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPageSelected(position: Int) {
|
||||||
|
pageSelection = position
|
||||||
|
(adapter as? LazyPagerAdapter)?.let {
|
||||||
|
it.swipeRefreshLayout?.isEnabled = scrollState == SCROLL_STATE_IDLE && it.enabledList[pageSelection, true]
|
||||||
|
val fragment = adapter?.instantiateItem(this@LazyViewPager, position)
|
||||||
|
val lazyFragment = fragment as? LazyFragment
|
||||||
|
lazyFragment?.createPage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -10,11 +10,10 @@ import android.view.LayoutInflater
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE
|
|
||||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial.Icon2
|
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial.Icon2
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
@ -23,7 +22,7 @@ import pl.szczodrzynski.edziennik.MainActivity.Companion.TARGET_GRADES_EDITOR
|
|||||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_GRADE
|
import pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_GRADE
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
||||||
import pl.szczodrzynski.edziennik.databinding.GradesFragmentBinding
|
import pl.szczodrzynski.edziennik.databinding.GradesListFragmentBinding
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradeDetailsDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradeDetailsDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog
|
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.GradesAverages
|
||||||
@ -36,24 +35,20 @@ import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
|
|||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
|
class GradesListFragment : Fragment(), CoroutineScope {
|
||||||
class GradesFragment : Fragment(), CoroutineScope {
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "GradesFragment"
|
private const val TAG = "GradesFragment"
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var app: App
|
private lateinit var app: App
|
||||||
private lateinit var activity: MainActivity
|
private lateinit var activity: MainActivity
|
||||||
private lateinit var b: GradesFragmentBinding
|
private lateinit var b: GradesListFragmentBinding
|
||||||
|
|
||||||
private val job: Job = Job()
|
private val job: Job = Job()
|
||||||
override val coroutineContext: CoroutineContext
|
override val coroutineContext: CoroutineContext
|
||||||
get() = job + Dispatchers.Main
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
// local/private variables go here
|
// local/private variables go here
|
||||||
private val adapter by lazy {
|
|
||||||
GradesAdapter(activity)
|
|
||||||
}
|
|
||||||
private val manager by lazy { app.gradesManager }
|
private val manager by lazy { app.gradesManager }
|
||||||
private val dontCountEnabled by lazy { manager.dontCountEnabled }
|
private val dontCountEnabled by lazy { manager.dontCountEnabled }
|
||||||
private val dontCountGrades by lazy { manager.dontCountGrades }
|
private val dontCountGrades by lazy { manager.dontCountGrades }
|
||||||
@ -63,51 +58,49 @@ class GradesFragment : Fragment(), CoroutineScope {
|
|||||||
activity = (getActivity() as MainActivity?) ?: return null
|
activity = (getActivity() as MainActivity?) ?: return null
|
||||||
context ?: return null
|
context ?: return null
|
||||||
app = activity.application as App
|
app = activity.application as App
|
||||||
b = GradesFragmentBinding.inflate(inflater)
|
b = GradesListFragmentBinding.inflate(inflater)
|
||||||
b.refreshLayout.setParent(activity.swipeRefreshLayout)
|
b.refreshLayout.setParent(activity.swipeRefreshLayout)
|
||||||
return b.root
|
return b.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { startCoroutineTimer(100L) {
|
||||||
if (!isAdded)
|
if (!isAdded) return@startCoroutineTimer
|
||||||
return
|
|
||||||
|
|
||||||
expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L
|
expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L
|
||||||
|
|
||||||
app.db.gradeDao()
|
val adapter = GradesAdapter(activity)
|
||||||
.getAllOrderBy(App.profileId, app.gradesManager.getOrderByString())
|
var firstRun = true
|
||||||
.observe(this, Observer { grades ->
|
|
||||||
if (b.gradesRecyclerView.adapter == null) {
|
|
||||||
b.gradesRecyclerView.adapter = adapter
|
|
||||||
b.gradesRecyclerView.apply {
|
|
||||||
setHasFixedSize(true)
|
|
||||||
layoutManager = LinearLayoutManager(context)
|
|
||||||
//addItemDecoration(SimpleDividerItemDecoration(context))
|
|
||||||
addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
|
||||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
|
||||||
if (recyclerView.canScrollVertically(-1)) {
|
|
||||||
b.refreshLayout.isEnabled = false
|
|
||||||
}
|
|
||||||
if (!recyclerView.canScrollVertically(-1) && newState == SCROLL_STATE_IDLE) {
|
|
||||||
b.refreshLayout.isEnabled = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
launch(Dispatchers.Default) {
|
app.db.gradeDao().getAllOrderBy(App.profileId, app.gradesManager.getOrderByString()).observe(this, Observer { items -> launch {
|
||||||
processGrades(grades)
|
if (!isAdded) return@launch
|
||||||
}
|
|
||||||
|
|
||||||
if (grades != null && grades.isNotEmpty()) {
|
// load & configure the adapter
|
||||||
b.gradesRecyclerView.visibility = View.VISIBLE
|
adapter.items = withContext(Dispatchers.Default) { processGrades(items) }
|
||||||
b.gradesNoData.visibility = View.GONE
|
if (items.isNotNullNorEmpty() && b.list.adapter == null) {
|
||||||
} else {
|
b.list.adapter = adapter
|
||||||
b.gradesRecyclerView.visibility = View.GONE
|
b.list.apply {
|
||||||
b.gradesNoData.visibility = View.VISIBLE
|
setHasFixedSize(true)
|
||||||
}
|
layoutManager = LinearLayoutManager(context)
|
||||||
})
|
addOnScrollListener(b.refreshLayout.onScrollListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
|
||||||
|
if (firstRun) {
|
||||||
|
expandSubject(adapter)
|
||||||
|
firstRun = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// show/hide relevant views
|
||||||
|
b.progressBar.isVisible = false
|
||||||
|
if (items.isNullOrEmpty()) {
|
||||||
|
b.list.isVisible = false
|
||||||
|
b.noData.isVisible = true
|
||||||
|
} else {
|
||||||
|
b.list.isVisible = true
|
||||||
|
b.noData.isVisible = false
|
||||||
|
}
|
||||||
|
}})
|
||||||
|
|
||||||
adapter.onGradeClick = {
|
adapter.onGradeClick = {
|
||||||
GradeDetailsDialog(activity, it)
|
GradeDetailsDialog(activity, it)
|
||||||
@ -153,10 +146,30 @@ class GradesFragment : Fragment(), CoroutineScope {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
activity.gainAttention()
|
activity.gainAttention()
|
||||||
|
}}
|
||||||
|
|
||||||
|
private fun expandSubject(adapter: GradesAdapter) {
|
||||||
|
var expandSubjectModel: GradesSubject? = null
|
||||||
|
if (expandSubjectId != 0L) {
|
||||||
|
expandSubjectModel = adapter.items.firstOrNull { it is GradesSubject && it.subjectId == expandSubjectId } as? GradesSubject
|
||||||
|
adapter.expandModel(
|
||||||
|
model = expandSubjectModel,
|
||||||
|
view = null,
|
||||||
|
notifyAdapter = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
startCoroutineTimer(500L) {
|
||||||
|
if (expandSubjectModel != null) {
|
||||||
|
b.list.smoothScrollToPosition(
|
||||||
|
adapter.items.indexOf(expandSubjectModel) + expandSubjectModel.semesters.size + (expandSubjectModel.semesters.firstOrNull()?.grades?.size ?: 0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("SuspendFunctionOnCoroutineScope")
|
@Suppress("SuspendFunctionOnCoroutineScope")
|
||||||
private suspend fun processGrades(grades: List<GradeFull>) {
|
private fun processGrades(grades: List<GradeFull>): MutableList<Any> {
|
||||||
val items = mutableListOf<GradesSubject>()
|
val items = mutableListOf<GradesSubject>()
|
||||||
|
|
||||||
var subjectId = -1L
|
var subjectId = -1L
|
||||||
@ -284,30 +297,7 @@ class GradesFragment : Fragment(), CoroutineScope {
|
|||||||
GradesManager.ORDER_BY_DATE_ASC -> items.sortBy { it.lastAddedDate }
|
GradesManager.ORDER_BY_DATE_ASC -> items.sortBy { it.lastAddedDate }
|
||||||
}
|
}
|
||||||
|
|
||||||
adapter.items = items.toMutableList()
|
return (items + stats).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) {
|
private fun countGrade(grade: Grade, averages: GradesAverages) {
|
@ -60,8 +60,11 @@ class HomeEventsCard(
|
|||||||
adapter = EventListAdapter(
|
adapter = EventListAdapter(
|
||||||
activity,
|
activity,
|
||||||
simpleMode = true,
|
simpleMode = true,
|
||||||
showDate = true,
|
|
||||||
showWeekDay = true,
|
showWeekDay = true,
|
||||||
|
showDate = true,
|
||||||
|
showType = true,
|
||||||
|
showTime = false,
|
||||||
|
showSubject = false,
|
||||||
onItemClick = {
|
onItemClick = {
|
||||||
EventDetailsDialog(
|
EventDetailsDialog(
|
||||||
activity,
|
activity,
|
||||||
@ -77,7 +80,7 @@ class HomeEventsCard(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
app.db.eventDao().getAllNearest(profile.id, Date.getToday(), 4).observe(activity, Observer { events ->
|
app.db.eventDao().getNearestNotDone(profile.id, Date.getToday(), 4).observe(activity, Observer { events ->
|
||||||
adapter.items = events
|
adapter.items = events
|
||||||
if (b.eventsView.adapter == null) {
|
if (b.eventsView.adapter == null) {
|
||||||
b.eventsView.adapter = adapter
|
b.eventsView.adapter = adapter
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-3-30.
|
||||||
|
*/
|
||||||
|
|
||||||
package pl.szczodrzynski.edziennik.ui.modules.homework
|
package pl.szczodrzynski.edziennik.ui.modules.homework
|
||||||
|
|
||||||
import android.os.AsyncTask
|
import android.os.AsyncTask
|
||||||
@ -7,46 +11,48 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.viewpager.widget.ViewPager
|
|
||||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||||
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
|
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
|
||||||
import pl.szczodrzynski.edziennik.App
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import pl.szczodrzynski.edziennik.MainActivity
|
import kotlinx.coroutines.Dispatchers
|
||||||
import pl.szczodrzynski.edziennik.R
|
import kotlinx.coroutines.Job
|
||||||
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||||
import pl.szczodrzynski.edziennik.databinding.FragmentHomeworkBinding
|
import pl.szczodrzynski.edziennik.databinding.HomeworkFragmentBinding
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
|
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.FragmentLazyPagerAdapter
|
||||||
import pl.szczodrzynski.edziennik.utils.Themes
|
|
||||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
||||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
|
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
class HomeworkFragment : Fragment() {
|
class HomeworkFragment : Fragment(), CoroutineScope {
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val TAG = "HomeworkFragment"
|
||||||
var pageSelection = 0
|
var pageSelection = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var app: App
|
private lateinit var app: App
|
||||||
private lateinit var activity: MainActivity
|
private lateinit var activity: MainActivity
|
||||||
private lateinit var b: FragmentHomeworkBinding
|
private lateinit var b: HomeworkFragmentBinding
|
||||||
|
|
||||||
|
private val job: Job = Job()
|
||||||
|
override val coroutineContext: CoroutineContext
|
||||||
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
|
// local/private variables go here
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
activity = (getActivity() as MainActivity?) ?: return null
|
activity = (getActivity() as MainActivity?) ?: return null
|
||||||
if (context == null)
|
context ?: return null
|
||||||
return null
|
|
||||||
app = activity.application as App
|
app = activity.application as App
|
||||||
context!!.theme.applyStyle(Themes.appTheme, true)
|
b = HomeworkFragmentBinding.inflate(inflater)
|
||||||
// activity, context and profile is valid
|
|
||||||
b = FragmentHomeworkBinding.inflate(inflater)
|
|
||||||
b.refreshLayout.setParent(activity.swipeRefreshLayout)
|
b.refreshLayout.setParent(activity.swipeRefreshLayout)
|
||||||
return b.root
|
return b.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
// TODO check if app, activity, b can be null
|
if (!isAdded) return
|
||||||
if (app.profile == null || !isAdded)
|
|
||||||
return
|
|
||||||
|
|
||||||
activity.bottomSheet.prependItems(
|
activity.bottomSheet.prependItems(
|
||||||
BottomSheetPrimaryItem(true)
|
BottomSheetPrimaryItem(true)
|
||||||
@ -67,34 +73,29 @@ class HomeworkFragment : Fragment() {
|
|||||||
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show()
|
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
b.viewPager.adapter = MessagesFragment.Adapter(childFragmentManager).also { adapter ->
|
val pagerAdapter = FragmentLazyPagerAdapter(
|
||||||
adapter.addFragment(HomeworkListFragment().also { fragment ->
|
fragmentManager ?: return,
|
||||||
fragment.arguments = Bundle().also { args ->
|
b.refreshLayout,
|
||||||
args.putInt("homeworkDate", HomeworkDate.CURRENT)
|
listOf(
|
||||||
}
|
HomeworkListFragment().apply {
|
||||||
}, getString(R.string.homework_tab_current))
|
arguments = Bundle("homeworkDate" to HomeworkDate.CURRENT)
|
||||||
|
} to getString(R.string.homework_tab_current),
|
||||||
|
|
||||||
adapter.addFragment(HomeworkListFragment().also { fragment ->
|
HomeworkListFragment().apply {
|
||||||
fragment.arguments = Bundle().also { args ->
|
arguments = Bundle("homeworkDate" to HomeworkDate.PAST)
|
||||||
args.putInt("homeworkDate", HomeworkDate.PAST)
|
} to getString(R.string.homework_tab_past)
|
||||||
}
|
)
|
||||||
}, getString(R.string.homework_tab_past))
|
)
|
||||||
|
b.viewPager.apply {
|
||||||
|
offscreenPageLimit = 1
|
||||||
|
adapter = pagerAdapter
|
||||||
|
currentItem = pageSelection
|
||||||
|
addOnPageSelectedListener {
|
||||||
|
pageSelection = it
|
||||||
|
}
|
||||||
|
b.tabLayout.setupWithViewPager(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
b.viewPager.currentItem = pageSelection
|
|
||||||
b.viewPager.clearOnPageChangeListeners()
|
|
||||||
b.viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
|
|
||||||
override fun onPageScrollStateChanged(state: Int) {
|
|
||||||
b.refreshLayout.isEnabled = state == ViewPager.SCROLL_STATE_IDLE
|
|
||||||
}
|
|
||||||
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
|
|
||||||
override fun onPageSelected(position: Int) {
|
|
||||||
pageSelection = position
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
b.tabLayout.setupWithViewPager(b.viewPager)
|
|
||||||
|
|
||||||
activity.navView.apply {
|
activity.navView.apply {
|
||||||
bottomBar.apply {
|
bottomBar.apply {
|
||||||
fabEnable = true
|
fabEnable = true
|
||||||
|
@ -4,67 +4,106 @@ import android.os.Bundle
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import pl.szczodrzynski.edziennik.App
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import pl.szczodrzynski.edziennik.MainActivity
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||||
import pl.szczodrzynski.edziennik.databinding.HomeworkListBinding
|
import pl.szczodrzynski.edziennik.databinding.HomeworkListFragmentBinding
|
||||||
import pl.szczodrzynski.edziennik.getInt
|
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.base.lazypager.LazyFragment
|
||||||
|
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
class HomeworkListFragment : Fragment() {
|
class HomeworkListFragment : LazyFragment(), CoroutineScope {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "HomeworkListFragment"
|
||||||
|
}
|
||||||
|
|
||||||
private lateinit var app: App
|
private lateinit var app: App
|
||||||
private lateinit var activity: MainActivity
|
private lateinit var activity: MainActivity
|
||||||
private lateinit var b: HomeworkListBinding
|
private lateinit var b: HomeworkListFragmentBinding
|
||||||
|
|
||||||
private var homeworkDate = HomeworkDate.CURRENT
|
private val job: Job = Job()
|
||||||
|
override val coroutineContext: CoroutineContext
|
||||||
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
|
// local/private variables go here
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
activity = (getActivity() as MainActivity?) ?: return null
|
activity = (getActivity() as MainActivity?) ?: return null
|
||||||
context ?: return null
|
context ?: return null
|
||||||
app = activity.application as App
|
app = activity.application as App
|
||||||
b = HomeworkListBinding.inflate(inflater)
|
b = HomeworkListFragmentBinding.inflate(inflater)
|
||||||
return b.root
|
return b.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onPageCreated(): Boolean { startCoroutineTimer(100L) {
|
||||||
// TODO check if app, activity, b can be null
|
val homeworkDate = arguments.getInt("homeworkDate", HomeworkDate.CURRENT)
|
||||||
if (!isAdded)
|
|
||||||
return
|
|
||||||
|
|
||||||
if (arguments != null) {
|
|
||||||
homeworkDate = arguments.getInt("homeworkDate", HomeworkDate.CURRENT)
|
|
||||||
}
|
|
||||||
|
|
||||||
val layoutManager = LinearLayoutManager(context)
|
|
||||||
layoutManager.reverseLayout = homeworkDate == HomeworkDate.PAST
|
|
||||||
layoutManager.stackFromEnd = homeworkDate == HomeworkDate.PAST
|
|
||||||
|
|
||||||
b.homeworkView.setHasFixedSize(true)
|
|
||||||
b.homeworkView.layoutManager = layoutManager
|
|
||||||
|
|
||||||
|
val today = Date.getToday()
|
||||||
val filter = when(homeworkDate) {
|
val filter = when(homeworkDate) {
|
||||||
HomeworkDate.CURRENT -> "eventDate >= '" + Date.getToday().stringY_m_d + "'"
|
HomeworkDate.CURRENT -> "eventDate >= '${today.stringY_m_d}' AND eventIsDone = 0"
|
||||||
else -> "eventDate < '" + Date.getToday().stringY_m_d + "'"
|
else -> "eventDate < '${today.stringY_m_d}' OR eventIsDone = 1"
|
||||||
}
|
}
|
||||||
|
|
||||||
app.db.eventDao()
|
val adapter = EventListAdapter(
|
||||||
.getAllByType(App.profileId, Event.TYPE_HOMEWORK, filter)
|
activity,
|
||||||
.observe(this, Observer { homeworkList ->
|
showWeekDay = true,
|
||||||
if (!isAdded) return@Observer
|
showDate = true,
|
||||||
|
showType = false,
|
||||||
|
showTime = true,
|
||||||
|
showSubject = true,
|
||||||
|
onItemClick = {
|
||||||
|
EventDetailsDialog(
|
||||||
|
activity,
|
||||||
|
it
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onEventEditClick = {
|
||||||
|
EventManualDialog(
|
||||||
|
activity,
|
||||||
|
it.profileId,
|
||||||
|
editingEvent = it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
if (homeworkList != null && homeworkList.size > 0) {
|
app.db.eventDao().getAllByType(App.profileId, Event.TYPE_HOMEWORK, filter).observe(this@HomeworkListFragment, Observer { items ->
|
||||||
val adapter = HomeworkAdapter(context, homeworkList)
|
if (!isAdded) return@Observer
|
||||||
b.homeworkView.adapter = adapter
|
|
||||||
b.homeworkView.visibility = View.VISIBLE
|
// load & configure the adapter
|
||||||
b.homeworkNoData.visibility = View.GONE
|
adapter.items = items
|
||||||
} else {
|
if (items.isNotNullNorEmpty() && b.list.adapter == null) {
|
||||||
b.homeworkView.visibility = View.GONE
|
b.list.adapter = adapter
|
||||||
b.homeworkNoData.visibility = View.VISIBLE
|
b.list.apply {
|
||||||
|
setHasFixedSize(true)
|
||||||
|
layoutManager = LinearLayoutManager(context).apply {
|
||||||
|
reverseLayout = homeworkDate == HomeworkDate.PAST
|
||||||
|
stackFromEnd = homeworkDate == HomeworkDate.PAST
|
||||||
}
|
}
|
||||||
})
|
addItemDecoration(SimpleDividerItemDecoration(context))
|
||||||
}
|
addOnScrollListener(onScrollListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
setSwipeToRefresh(false) // TODO
|
||||||
|
|
||||||
|
// show/hide relevant views
|
||||||
|
b.progressBar.isVisible = false
|
||||||
|
if (items.isNullOrEmpty()) {
|
||||||
|
b.list.isVisible = false
|
||||||
|
b.noData.isVisible = true
|
||||||
|
} else {
|
||||||
|
b.list.isVisible = true
|
||||||
|
b.noData.isVisible = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}; return true }
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import android.widget.ProgressBar
|
import android.widget.ProgressBar
|
||||||
|
import androidx.appcompat.widget.PopupMenu
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
import com.mikepenz.iconics.IconicsColor
|
import com.mikepenz.iconics.IconicsColor
|
||||||
@ -62,7 +63,7 @@ class MessageFragment : Fragment(), CoroutineScope {
|
|||||||
private lateinit var activity: MainActivity
|
private lateinit var activity: MainActivity
|
||||||
private lateinit var b: MessageFragmentBinding
|
private lateinit var b: MessageFragmentBinding
|
||||||
|
|
||||||
private lateinit var job: Job
|
private val job: Job = Job()
|
||||||
override val coroutineContext: CoroutineContext
|
override val coroutineContext: CoroutineContext
|
||||||
get() = job + Dispatchers.Main
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
@ -73,16 +74,12 @@ class MessageFragment : Fragment(), CoroutineScope {
|
|||||||
activity = (getActivity() as MainActivity?) ?: return null
|
activity = (getActivity() as MainActivity?) ?: return null
|
||||||
context ?: return null
|
context ?: return null
|
||||||
app = activity.application as App
|
app = activity.application as App
|
||||||
context!!.theme.applyStyle(Themes.appTheme, true)
|
|
||||||
b = MessageFragmentBinding.inflate(inflater)
|
b = MessageFragmentBinding.inflate(inflater)
|
||||||
job = Job()
|
|
||||||
return b.root
|
return b.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
// TODO check if app, activity, b can be null
|
if (!isAdded) return
|
||||||
if (app.profile == null || !isAdded)
|
|
||||||
return
|
|
||||||
|
|
||||||
b.closeButton.setImageDrawable(
|
b.closeButton.setImageDrawable(
|
||||||
IconicsDrawable(activity, CommunityMaterial.Icon2.cmd_window_close)
|
IconicsDrawable(activity, CommunityMaterial.Icon2.cmd_window_close)
|
||||||
@ -260,16 +257,35 @@ class MessageFragment : Fragment(), CoroutineScope {
|
|||||||
MessagesFragment.pageSelection = min(message.type, 1)
|
MessagesFragment.pageSelection = min(message.type, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val attachmentOnClick = { v: View ->
|
||||||
|
if (v.tag is Int) {
|
||||||
|
downloadAttachment(v.tag as Int)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val attachmentOnLongClick = { v: View ->
|
||||||
|
(v.tag as? Int)?.let { tag ->
|
||||||
|
val popupMenu = PopupMenu(v.context, v)
|
||||||
|
popupMenu.menu.add(0, tag, 0, R.string.messages_attachment_download_again)
|
||||||
|
popupMenu.setOnMenuItemClickListener {
|
||||||
|
downloadAttachment(it.itemId, forceDownload = true)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
popupMenu.show()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
private fun showAttachments() {
|
private fun showAttachments() {
|
||||||
if (message.attachmentIds != null) {
|
if (message.attachmentIds != null) {
|
||||||
val insertPoint = b.attachments
|
val insertPoint = b.attachments
|
||||||
insertPoint.removeAllViews()
|
insertPoint.removeAllViews()
|
||||||
|
|
||||||
val chipLayoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
val chipLayoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||||
chipLayoutParams.setMargins(0, Utils.dpToPx(8), 0, Utils.dpToPx(8))
|
chipLayoutParams.setMargins(0, 8.dp, 0, 0)
|
||||||
|
|
||||||
val progressLayoutParams = FrameLayout.LayoutParams(Utils.dpToPx(18), Utils.dpToPx(18))
|
val progressLayoutParams = FrameLayout.LayoutParams(18.dp, 18.dp)
|
||||||
progressLayoutParams.setMargins(Utils.dpToPx(8), 0, Utils.dpToPx(8), 0)
|
progressLayoutParams.setMargins(8.dp, 0, 8.dp, 0)
|
||||||
progressLayoutParams.gravity = END or CENTER_VERTICAL
|
progressLayoutParams.gravity = END or CENTER_VERTICAL
|
||||||
|
|
||||||
// CREATE VIEWS AND AN OBJECT FOR EVERY ATTACHMENT
|
// CREATE VIEWS AND AN OBJECT FOR EVERY ATTACHMENT
|
||||||
@ -280,12 +296,13 @@ class MessageFragment : Fragment(), CoroutineScope {
|
|||||||
val size = message.attachmentSizes[index]
|
val size = message.attachmentSizes[index]
|
||||||
// create the parent
|
// create the parent
|
||||||
val attachmentLayout = FrameLayout(b.root.context)
|
val attachmentLayout = FrameLayout(b.root.context)
|
||||||
attachmentLayout.setPadding(Utils.dpToPx(16), 0, Utils.dpToPx(16), 0)
|
attachmentLayout.setPadding(16.dp, 0, 16.dp, 0)
|
||||||
|
|
||||||
val attachmentChip = Chip(attachmentLayout.context)
|
val attachmentChip = Chip(attachmentLayout.context)
|
||||||
//attachmentChip.setChipBackgroundColorResource(ThemeUtils.getChipColorRes());
|
//attachmentChip.setChipBackgroundColorResource(ThemeUtils.getChipColorRes());
|
||||||
attachmentChip.layoutParams = chipLayoutParams
|
attachmentChip.layoutParams = chipLayoutParams
|
||||||
attachmentChip.height = Utils.dpToPx(40)
|
attachmentChip.chipMinHeight = 40.dp.toFloat()
|
||||||
|
//attachmentChip.height = Utils.dpToPx(40)
|
||||||
|
|
||||||
// show the file size or not
|
// show the file size or not
|
||||||
if (size == -1L)
|
if (size == -1L)
|
||||||
@ -312,11 +329,8 @@ class MessageFragment : Fragment(), CoroutineScope {
|
|||||||
attachmentChip.isCloseIconVisible = false
|
attachmentChip.isCloseIconVisible = false
|
||||||
// set the object's index in the attachmentList as the tag
|
// set the object's index in the attachmentList as the tag
|
||||||
attachmentChip.tag = index
|
attachmentChip.tag = index
|
||||||
attachmentChip.setOnClickListener { v ->
|
attachmentChip.onClick(attachmentOnClick)
|
||||||
if (v.tag is Int) {
|
attachmentChip.onLongClick(attachmentOnLongClick)
|
||||||
downloadAttachment(v.tag as Int)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
attachmentLayout.addView(attachmentChip)
|
attachmentLayout.addView(attachmentChip)
|
||||||
|
|
||||||
val attachmentProgress = ProgressBar(attachmentLayout.context)
|
val attachmentProgress = ProgressBar(attachmentLayout.context)
|
||||||
@ -338,10 +352,10 @@ class MessageFragment : Fragment(), CoroutineScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun downloadAttachment(index: Int) {
|
private fun downloadAttachment(index: Int, forceDownload: Boolean = false) {
|
||||||
val attachment = attachmentList[index]
|
val attachment = attachmentList[index]
|
||||||
|
|
||||||
if (attachment.downloaded != null) {
|
if (!forceDownload && attachment.downloaded != null) {
|
||||||
Utils.openFile(activity, File(attachment.downloaded))
|
Utils.openFile(activity, File(attachment.downloaded))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.fragment.app.FragmentPagerAdapter
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
import androidx.viewpager.widget.ViewPager
|
import androidx.viewpager.widget.ViewPager
|
||||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.App
|
||||||
@ -14,8 +14,9 @@ import pl.szczodrzynski.edziennik.MainActivity
|
|||||||
import pl.szczodrzynski.edziennik.R
|
import pl.szczodrzynski.edziennik.R
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||||
import pl.szczodrzynski.edziennik.databinding.FragmentMessagesBinding
|
import pl.szczodrzynski.edziennik.databinding.FragmentMessagesBinding
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyPagerAdapter
|
||||||
import pl.szczodrzynski.edziennik.utils.Themes
|
import pl.szczodrzynski.edziennik.utils.Themes
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class MessagesFragment : Fragment() {
|
class MessagesFragment : Fragment() {
|
||||||
companion object {
|
companion object {
|
||||||
@ -54,7 +55,7 @@ class MessagesFragment : Fragment() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
b.viewPager.adapter = Adapter(childFragmentManager).also { adapter ->
|
b.viewPager.adapter = Adapter(childFragmentManager, b.refreshLayout).also { adapter ->
|
||||||
|
|
||||||
adapter.addFragment(MessagesListFragment().also { fragment ->
|
adapter.addFragment(MessagesListFragment().also { fragment ->
|
||||||
fragment.arguments = Bundle().also { args ->
|
fragment.arguments = Bundle().also { args ->
|
||||||
@ -71,13 +72,8 @@ class MessagesFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
b.viewPager.currentItem = pageSelection
|
b.viewPager.currentItem = pageSelection
|
||||||
b.viewPager.clearOnPageChangeListeners()
|
|
||||||
b.viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
|
b.viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
|
||||||
override fun onPageScrollStateChanged(state: Int) {
|
override fun onPageScrollStateChanged(state: Int) {}
|
||||||
if (b.refreshLayout != null) {
|
|
||||||
b.refreshLayout.isEnabled = state == ViewPager.SCROLL_STATE_IDLE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
|
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
|
||||||
override fun onPageSelected(position: Int) {
|
override fun onPageSelected(position: Int) {
|
||||||
pageSelection = position
|
pageSelection = position
|
||||||
@ -126,11 +122,11 @@ class MessagesFragment : Fragment() {
|
|||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class Adapter(manager: FragmentManager) : FragmentPagerAdapter(manager) {
|
internal class Adapter(manager: FragmentManager, swipeRefreshLayout: SwipeRefreshLayout) : LazyPagerAdapter(manager, swipeRefreshLayout) {
|
||||||
private val mFragmentList = ArrayList<Fragment>()
|
private val mFragmentList = mutableListOf<LazyFragment>()
|
||||||
private val mFragmentTitleList = ArrayList<String>()
|
private val mFragmentTitleList = mutableListOf<String>()
|
||||||
|
|
||||||
override fun getItem(position: Int): Fragment {
|
override fun getPage(position: Int): LazyFragment {
|
||||||
return mFragmentList[position]
|
return mFragmentList[position]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,12 +134,12 @@ class MessagesFragment : Fragment() {
|
|||||||
return mFragmentList.size
|
return mFragmentList.size
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addFragment(fragment: Fragment, title: String) {
|
fun addFragment(fragment: LazyFragment, title: String) {
|
||||||
mFragmentList.add(fragment)
|
mFragmentList.add(fragment)
|
||||||
mFragmentTitleList.add(title)
|
mFragmentTitleList.add(title)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getPageTitle(position: Int): CharSequence? {
|
override fun getPageTitle(position: Int): CharSequence {
|
||||||
return mFragmentTitleList[position]
|
return mFragmentTitleList[position]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,11 +10,10 @@ import android.view.ViewGroup;
|
|||||||
import android.view.animation.Interpolator;
|
import android.view.animation.Interpolator;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.databinding.DataBindingUtil;
|
import androidx.databinding.DataBindingUtil;
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -26,13 +25,15 @@ import pl.szczodrzynski.edziennik.data.db.entity.Message;
|
|||||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull;
|
import pl.szczodrzynski.edziennik.data.db.full.MessageFull;
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.MessageRecipientFull;
|
import pl.szczodrzynski.edziennik.data.db.full.MessageRecipientFull;
|
||||||
import pl.szczodrzynski.edziennik.databinding.MessagesListBinding;
|
import pl.szczodrzynski.edziennik.databinding.MessagesListBinding;
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment;
|
||||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration;
|
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration;
|
||||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
import pl.szczodrzynski.edziennik.utils.Themes;
|
||||||
|
|
||||||
import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
|
import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
|
||||||
|
import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
|
||||||
import static pl.szczodrzynski.edziennik.utils.Utils.d;
|
import static pl.szczodrzynski.edziennik.utils.Utils.d;
|
||||||
|
|
||||||
public class MessagesListFragment extends Fragment {
|
public class MessagesListFragment extends LazyFragment {
|
||||||
|
|
||||||
private App app = null;
|
private App app = null;
|
||||||
private MainActivity activity = null;
|
private MainActivity activity = null;
|
||||||
@ -65,9 +66,9 @@ public class MessagesListFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
public boolean onPageCreated() {
|
||||||
if (app == null || activity == null || b == null || !isAdded())
|
if (app == null || activity == null || b == null || !isAdded())
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
long messageId = -1;
|
long messageId = -1;
|
||||||
if (getArguments() != null) {
|
if (getArguments() != null) {
|
||||||
@ -78,7 +79,7 @@ public class MessagesListFragment extends Fragment {
|
|||||||
args.putLong("messageId", messageId);
|
args.putLong("messageId", messageId);
|
||||||
getArguments().remove("messageId");
|
getArguments().remove("messageId");
|
||||||
activity.loadTarget(MainActivity.TARGET_MESSAGES_DETAILS, args);
|
activity.loadTarget(MainActivity.TARGET_MESSAGES_DETAILS, args);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getArguments() != null) {
|
if (getArguments() != null) {
|
||||||
@ -161,11 +162,22 @@ public class MessagesListFragment extends Fragment {
|
|||||||
// TODO ANIMATION
|
// TODO ANIMATION
|
||||||
//postponeEnterTransition();
|
//postponeEnterTransition();
|
||||||
|
|
||||||
viewParent = (ViewGroup) view.getParent();
|
viewParent = (ViewGroup) getView().getParent();
|
||||||
|
|
||||||
b.emailList.setLayoutManager(new LinearLayoutManager(view.getContext()));
|
b.emailList.setLayoutManager(new LinearLayoutManager(getView().getContext()));
|
||||||
b.emailList.addItemDecoration(new SimpleDividerItemDecoration(view.getContext()));
|
b.emailList.addItemDecoration(new SimpleDividerItemDecoration(getView().getContext()));
|
||||||
b.emailList.setAdapter(messagesAdapter);
|
b.emailList.setAdapter(messagesAdapter);
|
||||||
|
b.emailList.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||||
|
@Override
|
||||||
|
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
|
||||||
|
if (b.emailList.canScrollVertically(-1)) {
|
||||||
|
setSwipeToRefresh(false);
|
||||||
|
}
|
||||||
|
if (!b.emailList.canScrollVertically(-1) && newState == SCROLL_STATE_IDLE) {
|
||||||
|
setSwipeToRefresh(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (messageType == Message.TYPE_RECEIVED) {
|
if (messageType == Message.TYPE_RECEIVED) {
|
||||||
App.db.messageDao().getReceived(App.Companion.getProfileId()).observe(this, messageFulls -> {
|
App.db.messageDao().getReceived(App.Companion.getProfileId()).observe(this, messageFulls -> {
|
||||||
@ -215,7 +227,7 @@ public class MessagesListFragment extends Fragment {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createMessageList(List<MessageFull> messageFulls) {
|
private void createMessageList(List<MessageFull> messageFulls) {
|
||||||
|
@ -1,71 +1,62 @@
|
|||||||
package pl.szczodrzynski.edziennik.ui.modules.notifications
|
package pl.szczodrzynski.edziennik.ui.modules.notifications
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.TextView
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
import pl.szczodrzynski.edziennik.*
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
||||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
import pl.szczodrzynski.edziennik.databinding.NotificationsListItemBinding
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
class NotificationsAdapter(
|
class NotificationsAdapter(
|
||||||
private val context: Context
|
private val activity: AppCompatActivity,
|
||||||
) : RecyclerView.Adapter<NotificationsAdapter.ViewHolder>() {
|
val onItemClick: ((item: Notification) -> Unit)? = null
|
||||||
|
) : RecyclerView.Adapter<NotificationsAdapter.ViewHolder>(), CoroutineScope {
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "NotificationsAdapter"
|
private const val TAG = "NotificationsAdapter"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val app = activity.applicationContext as App
|
||||||
|
// optional: place the manager here
|
||||||
|
|
||||||
|
private val job = Job()
|
||||||
|
override val coroutineContext: CoroutineContext
|
||||||
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
var items = listOf<Notification>()
|
var items = listOf<Notification>()
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
val inflater = LayoutInflater.from(context)
|
val inflater = LayoutInflater.from(activity)
|
||||||
val view = inflater.inflate(R.layout.row_notifications_item, parent, false)
|
val view = NotificationsListItemBinding.inflate(inflater, parent, false)
|
||||||
return ViewHolder(view)
|
return ViewHolder(view)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
val app = context.applicationContext as App
|
val item = items[position]
|
||||||
|
val b = holder.b
|
||||||
|
|
||||||
val notification = items[position]
|
val date = Date.fromMillis(item.addedDate).formattedString
|
||||||
|
val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(activity)
|
||||||
|
|
||||||
val date = Date.fromMillis(notification.addedDate).formattedString
|
b.title.text = item.text
|
||||||
val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(context)
|
b.profileDate.text = listOf(
|
||||||
|
item.profileName ?: "",
|
||||||
holder.title.text = notification.text
|
|
||||||
holder.profileDate.text = listOf(
|
|
||||||
notification.profileName ?: "",
|
|
||||||
" • ",
|
" • ",
|
||||||
date.asColoredSpannable(colorSecondary)
|
date
|
||||||
).concat()
|
).concat().asColoredSpannable(colorSecondary)
|
||||||
holder.type.text = context.getNotificationTitle(notification.type)
|
b.type.text = activity.getNotificationTitle(item.type)
|
||||||
|
|
||||||
holder.root.onClick {
|
onItemClick?.let { listener ->
|
||||||
val intent = Intent("android.intent.action.MAIN")
|
b.root.onClick { listener(item) }
|
||||||
notification.fillIntent(intent)
|
|
||||||
|
|
||||||
d(TAG, "notification with item " + notification.viewId + " extras " + if (intent.extras == null) "null" else intent.extras!!.toString())
|
|
||||||
|
|
||||||
//Log.d(TAG, "Got date "+intent.getLongExtra("timetableDate", 0));
|
|
||||||
|
|
||||||
if (notification.profileId != null && notification.profileId != -1 && notification.profileId != app.profile.id && context is Activity) {
|
|
||||||
Toast.makeText(app, app.getString(R.string.toast_changing_profile), Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
app.sendBroadcast(intent)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount() = items.size
|
override fun getItemCount() = items.size
|
||||||
|
|
||||||
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
class ViewHolder(val b: NotificationsListItemBinding) : RecyclerView.ViewHolder(b.root)
|
||||||
var root = itemView
|
|
||||||
var title: TextView = itemView.findViewById(R.id.title)
|
|
||||||
var profileDate: TextView = itemView.findViewById(R.id.profileDate)
|
|
||||||
var type: TextView = itemView.findViewById(R.id.type)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,87 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Kuba Szczodrzyński 2019-11-22.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package pl.szczodrzynski.edziennik.ui.modules.notifications
|
|
||||||
|
|
||||||
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 pl.szczodrzynski.edziennik.App
|
|
||||||
import pl.szczodrzynski.edziennik.MainActivity
|
|
||||||
import pl.szczodrzynski.edziennik.R
|
|
||||||
import pl.szczodrzynski.edziennik.databinding.FragmentNotificationsBinding
|
|
||||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
|
||||||
import pl.szczodrzynski.edziennik.utils.Themes
|
|
||||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
|
||||||
|
|
||||||
|
|
||||||
class NotificationsFragment : Fragment() {
|
|
||||||
private lateinit var app: App
|
|
||||||
private lateinit var activity: MainActivity
|
|
||||||
private lateinit var b: FragmentNotificationsBinding
|
|
||||||
|
|
||||||
private val adapter by lazy {
|
|
||||||
NotificationsAdapter(activity)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
|
||||||
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 = FragmentNotificationsBinding.inflate(inflater)
|
|
||||||
return b.root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
// TODO check if app, activity, b can be null
|
|
||||||
if (app.profile == null || !isAdded)
|
|
||||||
return
|
|
||||||
|
|
||||||
activity.bottomSheet.prependItems(
|
|
||||||
BottomSheetPrimaryItem(true)
|
|
||||||
.withTitle(R.string.menu_remove_notifications)
|
|
||||||
.withIcon(CommunityMaterial.Icon.cmd_delete_sweep_outline)
|
|
||||||
.withOnClickListener(View.OnClickListener {
|
|
||||||
activity.bottomSheet.close()
|
|
||||||
AsyncTask.execute { app.db.notificationDao().clearAll() }
|
|
||||||
Toast.makeText(activity, R.string.menu_remove_notifications_success, Toast.LENGTH_SHORT).show()
|
|
||||||
}))
|
|
||||||
|
|
||||||
app.db.notificationDao()
|
|
||||||
.getAll()
|
|
||||||
.observe(this, Observer { notifications ->
|
|
||||||
if (app.profile == null || !isAdded) return@Observer
|
|
||||||
|
|
||||||
adapter.items = notifications
|
|
||||||
if (b.notificationsView.adapter == null) {
|
|
||||||
b.notificationsView.adapter = adapter
|
|
||||||
b.notificationsView.apply {
|
|
||||||
setHasFixedSize(true)
|
|
||||||
layoutManager = LinearLayoutManager(context)
|
|
||||||
addItemDecoration(SimpleDividerItemDecoration(context))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
adapter.notifyDataSetChanged()
|
|
||||||
|
|
||||||
if (notifications != null && notifications.isNotEmpty()) {
|
|
||||||
b.notificationsView.visibility = View.VISIBLE
|
|
||||||
b.notificationsNoData.visibility = View.GONE
|
|
||||||
} else {
|
|
||||||
b.notificationsView.visibility = View.GONE
|
|
||||||
b.notificationsNoData.visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2019-11-22.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.modules.notifications
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
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.core.view.isVisible
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import pl.szczodrzynski.edziennik.*
|
||||||
|
import pl.szczodrzynski.edziennik.databinding.NotificationsListFragmentBinding
|
||||||
|
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||||
|
import pl.szczodrzynski.edziennik.utils.Utils
|
||||||
|
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
class NotificationsListFragment : Fragment(), CoroutineScope {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "NotificationsListFragment"
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var app: App
|
||||||
|
private lateinit var activity: MainActivity
|
||||||
|
private lateinit var b: NotificationsListFragmentBinding
|
||||||
|
|
||||||
|
private val job: Job = Job()
|
||||||
|
override val coroutineContext: CoroutineContext
|
||||||
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
|
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 = NotificationsListFragmentBinding.inflate(inflater)
|
||||||
|
return b.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { startCoroutineTimer(100L) {
|
||||||
|
if (!isAdded) return@startCoroutineTimer
|
||||||
|
|
||||||
|
activity.bottomSheet.prependItems(
|
||||||
|
BottomSheetPrimaryItem(true)
|
||||||
|
.withTitle(R.string.menu_remove_notifications)
|
||||||
|
.withIcon(CommunityMaterial.Icon.cmd_delete_sweep_outline)
|
||||||
|
.withOnClickListener(View.OnClickListener {
|
||||||
|
activity.bottomSheet.close()
|
||||||
|
AsyncTask.execute { app.db.notificationDao().clearAll() }
|
||||||
|
Toast.makeText(activity, R.string.menu_remove_notifications_success, Toast.LENGTH_SHORT).show()
|
||||||
|
}))
|
||||||
|
|
||||||
|
val adapter = NotificationsAdapter(activity) { notification ->
|
||||||
|
val intent = Intent("android.intent.action.MAIN")
|
||||||
|
notification.fillIntent(intent)
|
||||||
|
|
||||||
|
Utils.d(TAG, "notification with item " + notification.viewId + " extras " + if (intent.extras == null) "null" else intent.extras!!.toString())
|
||||||
|
if (notification.profileId != null && notification.profileId != -1 && notification.profileId != app.profile.id && context is Activity) {
|
||||||
|
Toast.makeText(app, app.getString(R.string.toast_changing_profile), Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
app.sendBroadcast(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.db.notificationDao().getAll().observe(this, Observer { items ->
|
||||||
|
if (!isAdded) return@Observer
|
||||||
|
|
||||||
|
// load & configure the adapter
|
||||||
|
adapter.items = items
|
||||||
|
if (items.isNotNullNorEmpty() && b.list.adapter == null) {
|
||||||
|
b.list.adapter = adapter
|
||||||
|
b.list.apply {
|
||||||
|
setHasFixedSize(true)
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
addItemDecoration(SimpleDividerItemDecoration(context))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
|
||||||
|
// show/hide relevant views
|
||||||
|
b.progressBar.isVisible = false
|
||||||
|
if (items.isNullOrEmpty()) {
|
||||||
|
b.list.isVisible = false
|
||||||
|
b.noData.isVisible = true
|
||||||
|
} else {
|
||||||
|
b.list.isVisible = true
|
||||||
|
b.noData.isVisible = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-3-30.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.modules.template
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
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.*
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
||||||
|
import pl.szczodrzynski.edziennik.databinding.TemplateListItemBinding
|
||||||
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
class TemplateAdapter(
|
||||||
|
val activity: AppCompatActivity,
|
||||||
|
val onItemClick: ((item: Notification) -> Unit)? = null
|
||||||
|
) : RecyclerView.Adapter<TemplateAdapter.ViewHolder>(), CoroutineScope {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "TemplateAdapter"
|
||||||
|
}
|
||||||
|
|
||||||
|
private val app = activity.applicationContext as App
|
||||||
|
// optional: place the manager here
|
||||||
|
|
||||||
|
private val job = Job()
|
||||||
|
override val coroutineContext: CoroutineContext
|
||||||
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
|
var items = listOf<Notification>()
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
val inflater = LayoutInflater.from(parent.context)
|
||||||
|
val view = TemplateListItemBinding.inflate(inflater, parent, false)
|
||||||
|
return ViewHolder(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
val item = items[position]
|
||||||
|
val b = holder.b
|
||||||
|
|
||||||
|
val date = Date.fromMillis(item.addedDate).formattedString
|
||||||
|
val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(activity)
|
||||||
|
|
||||||
|
b.title.text = item.text
|
||||||
|
b.profileDate.text = listOf(
|
||||||
|
item.profileName ?: "",
|
||||||
|
" • ",
|
||||||
|
date
|
||||||
|
).concat().asColoredSpannable(colorSecondary)
|
||||||
|
b.type.text = activity.getNotificationTitle(item.type)
|
||||||
|
|
||||||
|
onItemClick?.let { listener ->
|
||||||
|
b.root.onClick { listener(item) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount() = items.size
|
||||||
|
|
||||||
|
class ViewHolder(val b: TemplateListItemBinding) : RecyclerView.ViewHolder(b.root)
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) Kuba Szczodrzyński 2020-1-8.
|
* Copyright (c) Kuba Szczodrzyński 2020-3-30.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pl.szczodrzynski.edziennik.ui.dialogs
|
package pl.szczodrzynski.edziennik.ui.modules.template
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-3-30.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.modules.template
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import pl.szczodrzynski.edziennik.App
|
||||||
|
import pl.szczodrzynski.edziennik.MainActivity
|
||||||
|
import pl.szczodrzynski.edziennik.addOnPageSelectedListener
|
||||||
|
import pl.szczodrzynski.edziennik.databinding.TemplateFragmentBinding
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.FragmentLazyPagerAdapter
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
class TemplateFragment : Fragment(), CoroutineScope {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "TemplateFragment"
|
||||||
|
var pageSelection = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var app: App
|
||||||
|
private lateinit var activity: MainActivity
|
||||||
|
private lateinit var b: TemplateFragmentBinding
|
||||||
|
|
||||||
|
private val job: Job = Job()
|
||||||
|
override val coroutineContext: CoroutineContext
|
||||||
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
|
// local/private variables go here
|
||||||
|
|
||||||
|
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 = TemplateFragmentBinding.inflate(inflater)
|
||||||
|
b.refreshLayout.setParent(activity.swipeRefreshLayout)
|
||||||
|
return b.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
if (!isAdded) return
|
||||||
|
|
||||||
|
val pagerAdapter = FragmentLazyPagerAdapter(
|
||||||
|
fragmentManager ?: return,
|
||||||
|
b.refreshLayout,
|
||||||
|
listOf(
|
||||||
|
TemplatePageFragment() to "Pager 0",
|
||||||
|
TemplatePageFragment() to "Pager 1",
|
||||||
|
TemplatePageFragment() to "Pager 2",
|
||||||
|
TemplatePageFragment() to "Pager 3",
|
||||||
|
TemplateListPageFragment() to "Pager 4",
|
||||||
|
TemplateListPageFragment() to "Pager 5",
|
||||||
|
TemplateListPageFragment() to "Pager 6",
|
||||||
|
TemplateListPageFragment() to "Pager 7"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
b.viewPager.apply {
|
||||||
|
offscreenPageLimit = 1
|
||||||
|
adapter = pagerAdapter
|
||||||
|
currentItem = pageSelection
|
||||||
|
addOnPageSelectedListener {
|
||||||
|
HomeworkFragment.pageSelection = it
|
||||||
|
}
|
||||||
|
b.tabLayout.setupWithViewPager(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-3-30.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.modules.template
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import pl.szczodrzynski.edziennik.*
|
||||||
|
import pl.szczodrzynski.edziennik.databinding.TemplateListFragmentBinding
|
||||||
|
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
class TemplateListFragment : Fragment(), CoroutineScope {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "TemplateListFragment"
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var app: App
|
||||||
|
private lateinit var activity: MainActivity
|
||||||
|
private lateinit var b: TemplateListFragmentBinding
|
||||||
|
|
||||||
|
private val job: Job = Job()
|
||||||
|
override val coroutineContext: CoroutineContext
|
||||||
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
|
// local/private variables go here
|
||||||
|
|
||||||
|
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 = TemplateListFragmentBinding.inflate(inflater)
|
||||||
|
b.refreshLayout.setParent(activity.swipeRefreshLayout)
|
||||||
|
return b.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { startCoroutineTimer(100L) {
|
||||||
|
if (!isAdded) return@startCoroutineTimer
|
||||||
|
|
||||||
|
val adapter = TemplateAdapter(activity)
|
||||||
|
|
||||||
|
app.db.notificationDao().getAll().observe(this, Observer { items ->
|
||||||
|
if (!isAdded) return@Observer
|
||||||
|
|
||||||
|
// load & configure the adapter
|
||||||
|
adapter.items = items
|
||||||
|
if (items.isNotNullNorEmpty() && b.list.adapter == null) {
|
||||||
|
b.list.adapter = adapter
|
||||||
|
b.list.apply {
|
||||||
|
setHasFixedSize(true)
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
addItemDecoration(SimpleDividerItemDecoration(context))
|
||||||
|
addOnScrollListener(b.refreshLayout.onScrollListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
b.refreshLayout.isEnabled = false // TODO
|
||||||
|
|
||||||
|
// show/hide relevant views
|
||||||
|
b.progressBar.isVisible = false
|
||||||
|
if (items.isNullOrEmpty()) {
|
||||||
|
b.list.isVisible = false
|
||||||
|
b.noData.isVisible = true
|
||||||
|
} else {
|
||||||
|
b.list.isVisible = true
|
||||||
|
b.noData.isVisible = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-3-30.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.modules.template
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import pl.szczodrzynski.edziennik.App
|
||||||
|
import pl.szczodrzynski.edziennik.MainActivity
|
||||||
|
import pl.szczodrzynski.edziennik.databinding.TemplateListPageFragmentBinding
|
||||||
|
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
|
||||||
|
import pl.szczodrzynski.edziennik.startCoroutineTimer
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
|
||||||
|
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
class TemplateListPageFragment : LazyFragment(), CoroutineScope {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "TemplateListPagerFragment"
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var app: App
|
||||||
|
private lateinit var activity: MainActivity
|
||||||
|
private lateinit var b: TemplateListPageFragmentBinding
|
||||||
|
|
||||||
|
private val job: Job = Job()
|
||||||
|
override val coroutineContext: CoroutineContext
|
||||||
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
|
// local/private variables go here
|
||||||
|
|
||||||
|
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 = TemplateListPageFragmentBinding.inflate(inflater)
|
||||||
|
return b.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPageCreated(): Boolean { startCoroutineTimer(100L) {
|
||||||
|
val adapter = TemplateAdapter(activity)
|
||||||
|
|
||||||
|
app.db.notificationDao().getAll().observe(this, Observer { items ->
|
||||||
|
if (!isAdded) return@Observer
|
||||||
|
|
||||||
|
// load & configure the adapter
|
||||||
|
adapter.items = items
|
||||||
|
if (items.isNotNullNorEmpty() && b.list.adapter == null) {
|
||||||
|
b.list.adapter = adapter
|
||||||
|
b.list.apply {
|
||||||
|
setHasFixedSize(true)
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
addItemDecoration(SimpleDividerItemDecoration(context))
|
||||||
|
addOnScrollListener(onScrollListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
setSwipeToRefresh(false) // TODO
|
||||||
|
|
||||||
|
// show/hide relevant views
|
||||||
|
b.progressBar.isVisible = false
|
||||||
|
if (items.isNullOrEmpty()) {
|
||||||
|
b.list.isVisible = false
|
||||||
|
b.noData.isVisible = true
|
||||||
|
} else {
|
||||||
|
b.list.isVisible = true
|
||||||
|
b.noData.isVisible = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}; return true }
|
||||||
|
}
|
@ -1,26 +1,30 @@
|
|||||||
package pl.szczodrzynski.edziennik.ui.modules.base
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-3-30.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.modules.template
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.App
|
||||||
import pl.szczodrzynski.edziennik.MainActivity
|
import pl.szczodrzynski.edziennik.MainActivity
|
||||||
import pl.szczodrzynski.edziennik.databinding.FragmentTemplateBinding
|
import pl.szczodrzynski.edziennik.databinding.TemplatePageFragmentBinding
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
class TemplateFragment : Fragment(), CoroutineScope {
|
class TemplatePageFragment : LazyFragment(), CoroutineScope {
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "TemplateFragment"
|
private const val TAG = "TemplatePagerFragment"
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var app: App
|
private lateinit var app: App
|
||||||
private lateinit var activity: MainActivity
|
private lateinit var activity: MainActivity
|
||||||
private lateinit var b: FragmentTemplateBinding
|
private lateinit var b: TemplatePageFragmentBinding
|
||||||
|
|
||||||
private val job: Job = Job()
|
private val job: Job = Job()
|
||||||
override val coroutineContext: CoroutineContext
|
override val coroutineContext: CoroutineContext
|
||||||
@ -32,15 +36,16 @@ class TemplateFragment : Fragment(), CoroutineScope {
|
|||||||
activity = (getActivity() as MainActivity?) ?: return null
|
activity = (getActivity() as MainActivity?) ?: return null
|
||||||
context ?: return null
|
context ?: return null
|
||||||
app = activity.application as App
|
app = activity.application as App
|
||||||
b = FragmentTemplateBinding.inflate(inflater)
|
b = TemplatePageFragmentBinding.inflate(inflater)
|
||||||
b.refreshLayout.setParent(activity.swipeRefreshLayout)
|
|
||||||
return b.root
|
return b.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onPageCreated(): Boolean {
|
||||||
if (!isAdded)
|
b.text.text = "Fragment $position"
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
|
b.button.addOnCheckedChangeListener { button, isChecked ->
|
||||||
|
setSwipeToRefresh(isChecked)
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-3-30.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.modules.template
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentManager
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyPagerAdapter
|
||||||
|
|
||||||
|
class TemplatePagerAdapter(fragmentManager: FragmentManager, swipeRefreshLayout: SwipeRefreshLayout) : LazyPagerAdapter(fragmentManager, swipeRefreshLayout) {
|
||||||
|
override fun getPage(position: Int) = TemplatePageFragment()
|
||||||
|
override fun getPageTitle(position: Int) = "Page $position"
|
||||||
|
override fun getCount() = 10
|
||||||
|
}
|
@ -27,7 +27,7 @@ import pl.szczodrzynski.edziennik.data.db.full.LessonFull
|
|||||||
import pl.szczodrzynski.edziennik.databinding.TimetableLessonBinding
|
import pl.szczodrzynski.edziennik.databinding.TimetableLessonBinding
|
||||||
import pl.szczodrzynski.edziennik.databinding.TimetableNoTimetableBinding
|
import pl.szczodrzynski.edziennik.databinding.TimetableNoTimetableBinding
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.timetable.LessonDetailsDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.timetable.LessonDetailsDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.base.PagerFragment
|
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment.Companion.DEFAULT_END_HOUR
|
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment.Companion.DEFAULT_END_HOUR
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment.Companion.DEFAULT_START_HOUR
|
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment.Companion.DEFAULT_START_HOUR
|
||||||
import pl.szczodrzynski.edziennik.utils.ListenerScrollView
|
import pl.szczodrzynski.edziennik.utils.ListenerScrollView
|
||||||
@ -36,7 +36,7 @@ import java.util.*
|
|||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
class TimetableDayFragment : PagerFragment(), CoroutineScope {
|
class TimetableDayFragment : LazyFragment(), CoroutineScope {
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "TimetableDayFragment"
|
private const val TAG = "TimetableDayFragment"
|
||||||
}
|
}
|
||||||
@ -104,9 +104,6 @@ class TimetableDayFragment : PagerFragment(), CoroutineScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onPageCreated(): Boolean {
|
override fun onPageCreated(): Boolean {
|
||||||
if (!isAdded)
|
|
||||||
return false
|
|
||||||
|
|
||||||
// observe lesson database
|
// observe lesson database
|
||||||
app.db.timetableDao().getForDate(App.profileId, date).observe(this, Observer { lessons ->
|
app.db.timetableDao().getForDate(App.profileId, date).observe(this, Observer { lessons ->
|
||||||
launch {
|
launch {
|
||||||
|
@ -5,9 +5,9 @@
|
|||||||
package pl.szczodrzynski.edziennik.ui.modules.timetable
|
package pl.szczodrzynski.edziennik.ui.modules.timetable
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.fragment.app.FragmentStatePagerAdapter
|
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyPagerAdapter
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ class TimetablePagerAdapter(
|
|||||||
private val items: List<Date>,
|
private val items: List<Date>,
|
||||||
private val startHour: Int,
|
private val startHour: Int,
|
||||||
private val endHour: Int
|
private val endHour: Int
|
||||||
) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
|
) : LazyPagerAdapter(fragmentManager, null) {
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "TimetablePagerAdapter"
|
private const val TAG = "TimetablePagerAdapter"
|
||||||
}
|
}
|
||||||
@ -25,7 +25,7 @@ class TimetablePagerAdapter(
|
|||||||
private val weekStart by lazy { today.weekStart }
|
private val weekStart by lazy { today.weekStart }
|
||||||
private val weekEnd by lazy { weekStart.clone().stepForward(0, 0, 6) }
|
private val weekEnd by lazy { weekStart.clone().stepForward(0, 0, 6) }
|
||||||
|
|
||||||
override fun getItem(position: Int): Fragment {
|
override fun getPage(position: Int): LazyFragment {
|
||||||
return TimetableDayFragment().apply {
|
return TimetableDayFragment().apply {
|
||||||
arguments = Bundle().apply {
|
arguments = Bundle().apply {
|
||||||
putInt("date", items[position].value)
|
putInt("date", items[position].value)
|
||||||
@ -39,7 +39,7 @@ class TimetablePagerAdapter(
|
|||||||
return items.size
|
return items.size
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getPageTitle(position: Int): CharSequence? {
|
override fun getPageTitle(position: Int): CharSequence {
|
||||||
val date = items[position]
|
val date = items[position]
|
||||||
val pageTitle = StringBuilder(Week.getFullDayName(date.weekDay))
|
val pageTitle = StringBuilder(Week.getFullDayName(date.weekDay))
|
||||||
if (date > weekEnd || date < weekStart) {
|
if (date > weekEnd || date < weekStart) {
|
||||||
|
@ -137,7 +137,7 @@ public class Date implements Comparable<Date> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static int diffDays(Date d1, Date d2) {
|
public static int diffDays(Date d1, Date d2) {
|
||||||
return (int) ((d1.getInMillis() - d2.getInMillis()) / (24 * 60 * 60 * 1000));
|
return Math.round((d1.getInMillis() - d2.getInMillis()) / (24 * 60 * 60 * 1000f));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isToday(Date date) {
|
public static boolean isToday(Date date) {
|
||||||
|
34
app/src/main/res/drawable/ic_no_homework.xml
Normal file
34
app/src/main/res/drawable/ic_no_homework.xml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-3-30.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="128dp"
|
||||||
|
android:height="128dp"
|
||||||
|
android:viewportWidth="128"
|
||||||
|
android:viewportHeight="128">
|
||||||
|
<path
|
||||||
|
android:pathData="m102,24h-74c-4.42,0 -8,3.58 -8,8v72c0,4.42 3.58,8 8,8h74c4.42,0 8,-3.58 8,-8v-72c0,-4.42 -3.58,-8 -8,-8z"
|
||||||
|
android:fillColor="#70d0f3"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m20,32v8h90v-8c0,-4.42 -3.58,-8 -8,-8h-74c-4.42,0 -8,3.58 -8,8z"
|
||||||
|
android:fillColor="#3281e6"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m88,92h-30c-2.21,0 -4,-1.79 -4,-4 0,-2.21 1.79,-4 4,-4h30c2.21,0 4,1.79 4,4 0,2.21 -1.79,4 -4,4zM92,74c0,-2.21 -1.79,-4 -4,-4h-30c-2.21,0 -4,1.79 -4,4 0,2.21 1.79,4 4,4h30c2.21,0 4,-1.79 4,-4zM92,60c0,-2.21 -1.79,-4 -4,-4h-30c-2.21,0 -4,1.79 -4,4 0,2.21 1.79,4 4,4h30c2.21,0 4,-1.79 4,-4zM48,88c0,-2.21 -1.79,-4 -4,-4h-2c-2.21,0 -4,1.79 -4,4 0,2.21 1.79,4 4,4h2c2.21,0 4,-1.79 4,-4zM48,74c0,-2.21 -1.79,-4 -4,-4h-2c-2.21,0 -4,1.79 -4,4 0,2.21 1.79,4 4,4h2c2.21,0 4,-1.79 4,-4zM48,60c0,-2.21 -1.79,-4 -4,-4h-2c-2.21,0 -4,1.79 -4,4 0,2.21 1.79,4 4,4h2c2.21,0 4,-1.79 4,-4z"
|
||||||
|
android:fillColor="#2b83d5"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m127,86.3 l-7.42,-7.42c-1.12,-1.13 -2.96,-1.13 -4.09,0l-3.5,3.49 11.5,11.5 3.49,-3.5c1.13,-1.13 1.13,-2.96 0,-4.09"
|
||||||
|
android:fillColor="#fc657c"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m93.4,124 l-11.5,-11.5 24.5,-24.5 11.5,11.5z"
|
||||||
|
android:fillColor="#ffa859"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m118,99.6 l-11.5,-11.5 5.75,-5.76 11.5,11.5z"
|
||||||
|
android:fillColor="#c8c8c8"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m81.9,113 l-3.88,15.4 15.4,-3.88z"
|
||||||
|
android:fillColor="#ffcd98"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m80,120 l-1.96,7.78 7.78,-1.96z"
|
||||||
|
android:fillColor="#818181"/>
|
||||||
|
</vector>
|
@ -195,6 +195,18 @@
|
|||||||
android:text="\uFCDA"
|
android:text="\uFCDA"
|
||||||
android:textSize="20sp"
|
android:textSize="20sp"
|
||||||
android:fontFamily="@font/community_material_font_v3_5_95_1" />
|
android:fontFamily="@font/community_material_font_v3_5_95_1" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/checkDoneButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:minWidth="0dp"
|
||||||
|
android:checkable="true"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||||
|
android:text="\uFCE1"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:fontFamily="@font/community_material_font_v3_5_95_1" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
@ -4,7 +4,8 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<layout xmlns:tools="http://schemas.android.com/tools"
|
<layout xmlns:tools="http://schemas.android.com/tools"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<data>
|
<data>
|
||||||
<import type="android.view.View"/>
|
<import type="android.view.View"/>
|
||||||
@ -54,22 +55,35 @@
|
|||||||
android:id="@+id/topic"
|
android:id="@+id/topic"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:layout_marginRight="4dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:textAppearance="@style/NavView.TextView.Medium"
|
|
||||||
android:maxLines="@{simpleMode ? 2 : 3}"
|
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
tools:maxLines="3"
|
android:maxLines="3"
|
||||||
|
android:textAppearance="@style/NavView.TextView.Medium"
|
||||||
tools:text="Rozdział II: Panowanie Piastów i Jagiellonów.Przeniesiony z 11 grudnia. Nie wiem co się dzieje w tym roku nie będzie już religii w szkołach podstawowych w Polsce i Europie zachodniej Afryki" />
|
tools:text="Rozdział II: Panowanie Piastów i Jagiellonów.Przeniesiony z 11 grudnia. Nie wiem co się dzieje w tym roku nie będzie już religii w szkołach podstawowych w Polsce i Europie zachodniej Afryki" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/editButton"
|
android:id="@+id/editButton"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="@font/community_material_font_v3_5_95_1"
|
||||||
android:minWidth="0dp"
|
android:minWidth="0dp"
|
||||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
|
||||||
android:text="\uFC92"
|
android:text="\uFC92"
|
||||||
android:textSize="20sp"
|
android:textSize="20sp"
|
||||||
android:fontFamily="@font/community_material_font_v3_5_95_1"/>
|
tools:visibility="gone" />
|
||||||
|
|
||||||
|
<com.mikepenz.iconics.view.IconicsImageView
|
||||||
|
android:id="@+id/isDone"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_gravity="top"
|
||||||
|
android:layout_marginLeft="4dp"
|
||||||
|
android:layout_marginRight="4dp"
|
||||||
|
app:iiv_color="@color/md_green_500"
|
||||||
|
app:iiv_icon="cmd-check"
|
||||||
|
tools:background="@sample/check" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
app:tabSelectedTextColor="?colorPrimary"
|
app:tabSelectedTextColor="?colorPrimary"
|
||||||
app:tabTextColor="?android:textColorPrimary"/>
|
app:tabTextColor="?android:textColorPrimary"/>
|
||||||
|
|
||||||
<androidx.viewpager.widget.ViewPager
|
<pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyViewPager
|
||||||
android:id="@+id/viewPager"
|
android:id="@+id/viewPager"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@ -29,4 +29,4 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator>
|
</pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator>
|
||||||
</layout>
|
</layout>
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
android:id="@+id/tabLayout"
|
android:id="@+id/tabLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="48dp"
|
android:layout_height="48dp"
|
||||||
android:background="@color/colorSurface_6dp"
|
android:background="@color/colorSurface_1dp"
|
||||||
app:rtl_tabIndicatorColor="?colorPrimary"
|
app:rtl_tabIndicatorColor="?colorPrimary"
|
||||||
app:rtl_tabMaxWidth="300dp"
|
app:rtl_tabMaxWidth="300dp"
|
||||||
app:rtl_tabMinWidth="90dp"
|
app:rtl_tabMinWidth="90dp"
|
||||||
@ -40,7 +40,7 @@
|
|||||||
app:rtl_tabTextAppearance="@style/rtl_RecyclerTabLayout.Tab" />
|
app:rtl_tabTextAppearance="@style/rtl_RecyclerTabLayout.Tab" />
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<androidx.viewpager.widget.ViewPager
|
<pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyViewPager
|
||||||
android:id="@+id/viewPager"
|
android:id="@+id/viewPager"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@ -94,4 +94,4 @@
|
|||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
<!--</pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator>-->
|
<!--</pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator>-->
|
||||||
</layout>
|
</layout>
|
||||||
|
@ -16,8 +16,14 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progressBar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/gradesNoData"
|
android:id="@+id/noData"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
@ -25,13 +31,17 @@
|
|||||||
android:fontFamily="sans-serif-light"
|
android:fontFamily="sans-serif-light"
|
||||||
android:text="@string/grades_no_data"
|
android:text="@string/grades_no_data"
|
||||||
android:textSize="24sp"
|
android:textSize="24sp"
|
||||||
app:drawableTopCompat="@drawable/ic_no_grades" />
|
android:visibility="gone"
|
||||||
|
app:drawableTopCompat="@drawable/ic_no_grades"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/gradesRecyclerView"
|
android:id="@+id/list"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:listitem="@layout/grades_item_subject" />
|
android:visibility="gone"
|
||||||
|
tools:listitem="@layout/grades_item_subject"
|
||||||
|
tools:visibility="visible" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
</pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator>
|
</pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator>
|
||||||
</layout>
|
</layout>
|
@ -1,7 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-3-30.
|
||||||
|
-->
|
||||||
|
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator
|
<pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator
|
||||||
android:id="@+id/refreshLayout"
|
android:id="@+id/refreshLayout"
|
||||||
@ -23,12 +26,11 @@
|
|||||||
app:tabSelectedTextColor="?colorPrimary"
|
app:tabSelectedTextColor="?colorPrimary"
|
||||||
app:tabTextColor="?android:textColorPrimary" />
|
app:tabTextColor="?android:textColorPrimary" />
|
||||||
|
|
||||||
<androidx.viewpager.widget.ViewPager
|
<pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyViewPager
|
||||||
android:id="@+id/viewPager"
|
android:id="@+id/viewPager"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator>
|
</pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator>
|
||||||
</layout>
|
</layout>
|
@ -1,47 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<layout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/homeworkView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
</androidx.recyclerview.widget.RecyclerView>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/homeworkNoData"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:gravity="center"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:visibility="visible">
|
|
||||||
|
|
||||||
<com.mikepenz.iconics.view.IconicsImageView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="92dp"
|
|
||||||
app:iiv_color="?android:textColorPrimary"
|
|
||||||
app:iiv_icon="szf-file-document-edit"
|
|
||||||
app:iiv_size="92dp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="8dp"
|
|
||||||
android:fontFamily="sans-serif-medium"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="@string/homework_no_data"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:textStyle="italic" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</LinearLayout>
|
|
||||||
</layout>
|
|
41
app/src/main/res/layout/homework_list_fragment.xml
Normal file
41
app/src/main/res/layout/homework_list_fragment.xml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-3-30.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progressBar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/noData"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:drawablePadding="16dp"
|
||||||
|
android:fontFamily="sans-serif-light"
|
||||||
|
android:text="@string/homework_no_data"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:drawableTopCompat="@drawable/ic_no_homework"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:listitem="@layout/event_list_item"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
</FrameLayout>
|
||||||
|
</layout>
|
@ -231,8 +231,7 @@
|
|||||||
android:ellipsize="middle"
|
android:ellipsize="middle"
|
||||||
android:text="Wyniki sprawdzianu z matematyki.pdf"
|
android:text="Wyniki sprawdzianu z matematyki.pdf"
|
||||||
android:visibility="visible"
|
android:visibility="visible"
|
||||||
app:chipIcon="@drawable/googleg_standard_color_18"
|
app:chipIcon="@drawable/googleg_standard_color_18" />
|
||||||
app:chipMinHeight="36dp" />
|
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
style="?android:attr/progressBarStyle"
|
style="?android:attr/progressBarStyle"
|
||||||
|
@ -1,30 +1,41 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2019-11-22.
|
||||||
|
-->
|
||||||
|
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<ProgressBar
|
||||||
android:id="@+id/notificationsView"
|
android:id="@+id/progressBar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
tools:listitem="@layout/row_notifications_item"
|
android:layout_gravity="center" />
|
||||||
tools:visibility="gone" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/notificationsNoData"
|
android:id="@+id/noData"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
app:drawableTopCompat="@drawable/ic_no_notifications"
|
|
||||||
android:drawablePadding="16dp"
|
android:drawablePadding="16dp"
|
||||||
android:fontFamily="sans-serif-light"
|
android:fontFamily="sans-serif-light"
|
||||||
android:text="@string/notifications_no_data"
|
android:text="@string/notifications_no_data"
|
||||||
android:textSize="24sp"
|
android:textSize="24sp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
|
app:drawableTopCompat="@drawable/ic_no_notifications"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:listitem="@layout/notifications_list_item"
|
||||||
|
tools:visibility="visible" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
</layout>
|
</layout>
|
37
app/src/main/res/layout/notifications_list_item.xml
Normal file
37
app/src/main/res/layout/notifications_list_item.xml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/NavView.TextView.Medium"
|
||||||
|
tools:text="Dzisiaj 1 to szczęśliwy numerek" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/profileDate"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
tools:text="Władca Androida • 22 listopada" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/type"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:text="Szczęśliwy numerek" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</layout>
|
@ -1,35 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="8dp"
|
|
||||||
android:background="?selectableItemBackground">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/title"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textAppearance="@style/NavView.TextView.Medium"
|
|
||||||
tools:text="Dzisiaj 1 to szczęśliwy numerek" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/profileDate"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
tools:text="Władca Androida • 22 listopada" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/type"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
tools:text="Szczęśliwy numerek" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
36
app/src/main/res/layout/template_fragment.xml
Normal file
36
app/src/main/res/layout/template_fragment.xml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-3-30.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator
|
||||||
|
android:id="@+id/refreshLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabLayout
|
||||||
|
android:id="@+id/tabLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/colorSurface_6dp"
|
||||||
|
app:tabIndicatorColor="?colorPrimary"
|
||||||
|
app:tabMode="auto"
|
||||||
|
app:tabSelectedTextColor="?colorPrimary"
|
||||||
|
app:tabTextColor="?android:textColorPrimary" />
|
||||||
|
|
||||||
|
<pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyViewPager
|
||||||
|
android:id="@+id/viewPager"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
</LinearLayout>
|
||||||
|
</pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator>
|
||||||
|
</layout>
|
47
app/src/main/res/layout/template_list_fragment.xml
Normal file
47
app/src/main/res/layout/template_list_fragment.xml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-3-30.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator
|
||||||
|
android:id="@+id/refreshLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progressBar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/noData"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:drawablePadding="16dp"
|
||||||
|
android:fontFamily="sans-serif-light"
|
||||||
|
android:text="@string/grades_no_data"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:drawableTopCompat="@drawable/ic_no_grades"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:listitem="@layout/grades_item_subject"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
</FrameLayout>
|
||||||
|
</pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator>
|
||||||
|
</layout>
|
@ -3,16 +3,39 @@
|
|||||||
~ Copyright (c) Kuba Szczodrzyński 2019-12-19.
|
~ Copyright (c) Kuba Szczodrzyński 2019-12-19.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="8dp"
|
android:background="?selectableItemBackground"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:background="?selectableItemBackground">
|
android:padding="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/NavView.TextView.Medium"
|
||||||
|
tools:text="Dzisiaj 1 to szczęśliwy numerek" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/profileDate"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
tools:text="Władca Androida • 22 listopada" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/type"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:text="Szczęśliwy numerek" />
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</layout>
|
</layout>
|
||||||
|
41
app/src/main/res/layout/template_list_page_fragment.xml
Normal file
41
app/src/main/res/layout/template_list_page_fragment.xml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-3-30.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progressBar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/noData"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:drawablePadding="16dp"
|
||||||
|
android:fontFamily="sans-serif-light"
|
||||||
|
android:text="@string/grades_no_data"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:drawableTopCompat="@drawable/ic_no_grades"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:listitem="@layout/grades_item_subject"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
</FrameLayout>
|
||||||
|
</layout>
|
30
app/src/main/res/layout/template_page_fragment.xml
Normal file
30
app/src/main/res/layout/template_page_fragment.xml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-3-30.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="Fragment 1" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/button"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:checkable="true"
|
||||||
|
android:text="Enable/disable Swipe to refresh"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</layout>
|
@ -1278,4 +1278,7 @@
|
|||||||
<string name="registration_enable_dialog_title">Rejestracja na serwerze</string>
|
<string name="registration_enable_dialog_title">Rejestracja na serwerze</string>
|
||||||
<string name="registration_enable_dialog_text">Rejestracja jest automatyczna, jeśli ta opcja jest włączona. Pozwala na tworzenie i odbieranie wydarzeń udostępnionych innym uczniom z Twojej klasy. Dzięki temu, można dodawać do dziennika pozycje nie zapisane przez nauczyciela.\n\nUpewnij się, że zapoznałeś się z warunkami <a href="http://szkolny.eu/privacy-policy">Polityki prywatności</a> i akceptujesz jej postanowienia.</string>
|
<string name="registration_enable_dialog_text">Rejestracja jest automatyczna, jeśli ta opcja jest włączona. Pozwala na tworzenie i odbieranie wydarzeń udostępnionych innym uczniom z Twojej klasy. Dzięki temu, można dodawać do dziennika pozycje nie zapisane przez nauczyciela.\n\nUpewnij się, że zapoznałeś się z warunkami <a href="http://szkolny.eu/privacy-policy">Polityki prywatności</a> i akceptujesz jej postanowienia.</string>
|
||||||
<string name="menu_add_remove_cards">Dodaj lub usuń karty</string>
|
<string name="menu_add_remove_cards">Dodaj lub usuń karty</string>
|
||||||
|
<string name="menu_template">Template</string>
|
||||||
|
<string name="messages_attachment_download_again">Pobierz ponownie</string>
|
||||||
|
<string name="hint_mark_as_done">Oznacz jako wykonane</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -5,8 +5,8 @@ buildscript {
|
|||||||
kotlin_version = '1.3.61'
|
kotlin_version = '1.3.61'
|
||||||
|
|
||||||
release = [
|
release = [
|
||||||
versionName: "4.0-rc.3",
|
versionName: "4.0-rc.4",
|
||||||
versionCode: 4000039
|
versionCode: 4000049
|
||||||
]
|
]
|
||||||
|
|
||||||
setup = [
|
setup = [
|
||||||
|
1
codegen/.gitignore
vendored
Normal file
1
codegen/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/build
|
39
codegen/build.gradle
Normal file
39
codegen/build.gradle
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-3-28.
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply plugin: 'java-library'
|
||||||
|
apply plugin: 'kotlin'
|
||||||
|
apply plugin: 'kotlin-kapt'
|
||||||
|
|
||||||
|
kapt {
|
||||||
|
generateStubs = true
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDir "${buildDir.absolutePath}/tmp/kapt/main/kotlinGenerated/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
kapt project(":annotation")
|
||||||
|
compileOnly project(':annotation')
|
||||||
|
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
|
|
||||||
|
// configuration generator for service providers
|
||||||
|
implementation "com.google.auto.service:auto-service:1.0-rc4"
|
||||||
|
kapt "com.google.auto.service:auto-service:1.0-rc4"
|
||||||
|
kapt "androidx.room:room-compiler:${versions.room}"
|
||||||
|
implementation "androidx.room:room-runtime:${versions.room}"
|
||||||
|
implementation "com.squareup:kotlinpoet:1.5.0"
|
||||||
|
implementation "androidx.sqlite:sqlite:2.1.0@aar"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceCompatibility = "7"
|
||||||
|
targetCompatibility = "7"
|
@ -0,0 +1,339 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-3-28.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.codegen
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Ignore
|
||||||
|
import androidx.room.TypeConverters
|
||||||
|
import com.google.auto.service.AutoService
|
||||||
|
import com.squareup.kotlinpoet.*
|
||||||
|
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
|
||||||
|
import pl.szczodrzynski.edziennik.annotation.SelectiveDao
|
||||||
|
import pl.szczodrzynski.edziennik.annotation.UpdateSelective
|
||||||
|
import java.io.File
|
||||||
|
import javax.annotation.processing.*
|
||||||
|
import javax.lang.model.SourceVersion
|
||||||
|
import javax.lang.model.element.*
|
||||||
|
import javax.lang.model.type.*
|
||||||
|
import javax.lang.model.util.ElementFilter
|
||||||
|
import javax.tools.Diagnostic
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
@AutoService(Processor::class)
|
||||||
|
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||||
|
@SupportedOptions(FileGenerator.KAPT_KOTLIN_GENERATED_OPTION_NAME)
|
||||||
|
class FileGenerator : AbstractProcessor() {
|
||||||
|
companion object {
|
||||||
|
const val KAPT_KOTLIN_GENERATED_OPTION_NAME = "kapt.kotlin.generated"
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class TypeConverter(val dataType: TypeMirror, val converterType: TypeElement, val methodName: Name, val returnType: TypeMirror)
|
||||||
|
|
||||||
|
private inline fun <reified T : Annotation> Element.getAnnotationClassValue(f: T.() -> KClass<*>) = try {
|
||||||
|
getAnnotation(T::class.java).f()
|
||||||
|
throw Exception("Expected to get a MirroredTypeException")
|
||||||
|
} catch (e: MirroredTypeException) {
|
||||||
|
e.typeMirror
|
||||||
|
}
|
||||||
|
private inline fun <reified T : Annotation> Element.getAnnotationClassValues(f: T.() -> Array<KClass<*>>) = try {
|
||||||
|
getAnnotation(T::class.java).f()
|
||||||
|
throw Exception("Expected to get a MirroredTypesException")
|
||||||
|
} catch (e: MirroredTypesException) {
|
||||||
|
e.typeMirrors
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun process(set: MutableSet<out TypeElement>?, roundEnvironment: RoundEnvironment?): Boolean {
|
||||||
|
roundEnvironment?.getElementsAnnotatedWith(SelectiveDao::class.java)?.forEach { it ->
|
||||||
|
if (it.kind != ElementKind.CLASS) {
|
||||||
|
processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, "Can only be applied to classes, element: $it")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val generatedSourcesRoot = processingEnv.options[KAPT_KOTLIN_GENERATED_OPTION_NAME]
|
||||||
|
if (generatedSourcesRoot?.isEmpty() != false) {
|
||||||
|
processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, "Can't find the target directory for generated Kotlin files.")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val file = File(generatedSourcesRoot)
|
||||||
|
file.mkdirs()
|
||||||
|
|
||||||
|
val dao = it as TypeElement
|
||||||
|
processClass(dao, file)
|
||||||
|
|
||||||
|
//processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, "package = $packageName, className = $className, methodName = $methodName, tableName = $tableName, paramName = $paramName, paramClass = $paramClass")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processClass(dao: TypeElement, file: File) {
|
||||||
|
val daoName = dao.simpleName.toString()
|
||||||
|
val packageName = processingEnv.elementUtils.getPackageOf(dao).toString()
|
||||||
|
|
||||||
|
val dbType = processingEnv.typeUtils.asElement(dao.getAnnotationClassValue<SelectiveDao> { db }) as TypeElement
|
||||||
|
val typeConverters = dbType.getAnnotationClassValues<TypeConverters> { value }.map {
|
||||||
|
processingEnv.typeUtils.asElement(it) as TypeElement
|
||||||
|
}.map { type ->
|
||||||
|
processingEnv.elementUtils.getAllMembers(type).mapNotNull { element ->
|
||||||
|
if (element is ExecutableElement) {
|
||||||
|
if (element.returnType.toString() == "java.lang.String"
|
||||||
|
|| element.returnType.toString() == "java.lang.Long"
|
||||||
|
|| element.returnType.toString() == "java.lang.Integer"
|
||||||
|
|| element.returnType.kind.isPrimitive) {
|
||||||
|
if (element.simpleName.startsWith("to") && element.parameters.isNotEmpty())
|
||||||
|
return@mapNotNull TypeConverter(element.parameters.first().asType(), type, element.simpleName, element.returnType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}.flatten()
|
||||||
|
|
||||||
|
//processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, "c = ${typeConverters.joinToString()}")
|
||||||
|
|
||||||
|
val roomDatabase = ClassName("androidx.room", "RoomDatabase")
|
||||||
|
val selective = TypeSpec.classBuilder("${daoName}Selective")
|
||||||
|
.primaryConstructor(FunSpec.constructorBuilder()
|
||||||
|
.addParameter("__db", roomDatabase, KModifier.PRIVATE)
|
||||||
|
.build())
|
||||||
|
.addProperty(PropertySpec.builder("__db", roomDatabase)
|
||||||
|
.initializer("__db")
|
||||||
|
.addModifiers(KModifier.PRIVATE)
|
||||||
|
.build())
|
||||||
|
|
||||||
|
val usedTypeConverters = mutableSetOf<TypeConverter>()
|
||||||
|
|
||||||
|
processingEnv.elementUtils.getAllMembers(dao).forEach { element ->
|
||||||
|
if (element.kind != ElementKind.METHOD)
|
||||||
|
return@forEach
|
||||||
|
val method = element as ExecutableElement
|
||||||
|
val annotation = method.getAnnotation(UpdateSelective::class.java) ?: return@forEach
|
||||||
|
usedTypeConverters.addAll(processMethod(selective, method, annotation, typeConverters))
|
||||||
|
}
|
||||||
|
|
||||||
|
usedTypeConverters.forEach { converter ->
|
||||||
|
selective.addProperty(PropertySpec.builder("__${converter.converterType.simpleName}", converter.converterType.asType().asTypeName(), KModifier.PRIVATE)
|
||||||
|
.delegate(CodeBlock.builder()
|
||||||
|
.beginControlFlow("lazy")
|
||||||
|
.addStatement("%T()", converter.converterType.asType().asTypeName())
|
||||||
|
.endControlFlow()
|
||||||
|
.build())
|
||||||
|
.build())
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSpec.builder(packageName, "${daoName}Selective")
|
||||||
|
.addType(selective.build())
|
||||||
|
.build()
|
||||||
|
.writeTo(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun VariableElement.name() = getAnnotation(ColumnInfo::class.java)?.name ?: simpleName.toString()
|
||||||
|
|
||||||
|
private fun processMethod(cls: TypeSpec.Builder, method: ExecutableElement, annotation: UpdateSelective, typeConverters: List<TypeConverter>): List<TypeConverter> {
|
||||||
|
val methodName = method.simpleName.toString()
|
||||||
|
val parameter = method.parameters.first()
|
||||||
|
val paramName = parameter.simpleName.toString()
|
||||||
|
val paramTypeElement = processingEnv.typeUtils.asElement(parameter.asType()) as TypeElement
|
||||||
|
val paramTypeAnnotation = paramTypeElement.getAnnotation(Entity::class.java)
|
||||||
|
|
||||||
|
val tableName = paramTypeAnnotation.tableName
|
||||||
|
val primaryKeys = annotation.primaryKeys
|
||||||
|
val skippedColumns = annotation.skippedColumns
|
||||||
|
|
||||||
|
|
||||||
|
var members = processingEnv.elementUtils.getAllMembers(paramTypeElement)
|
||||||
|
val allFields = ElementFilter.fieldsIn(members)
|
||||||
|
|
||||||
|
// check all super classes
|
||||||
|
var superType = paramTypeElement.superclass
|
||||||
|
while (superType !is NoType) {
|
||||||
|
val superTypeElement = processingEnv.typeUtils.asElement(superType) as TypeElement
|
||||||
|
members = processingEnv.elementUtils.getAllMembers(superTypeElement)
|
||||||
|
allFields += ElementFilter.fieldsIn(members)
|
||||||
|
superType = superTypeElement.superclass
|
||||||
|
}
|
||||||
|
|
||||||
|
allFields.removeAll { skippedColumns.contains(it.name()) }
|
||||||
|
allFields.removeAll { it.getAnnotation(Ignore::class.java) != null }
|
||||||
|
allFields.removeAll { field -> field.modifiers.any { it == Modifier.STATIC || it == Modifier.FINAL } }
|
||||||
|
val allFieldsDistinct = allFields.distinct()
|
||||||
|
|
||||||
|
val fields = allFieldsDistinct.filterNot { primaryKeys.contains(it.name()) }
|
||||||
|
val primaryFields = allFieldsDistinct.filter { primaryKeys.contains(it.name()) }
|
||||||
|
val fieldNames = fields.map { it.name() }
|
||||||
|
val primaryFieldNames = primaryFields.map { it.name() }
|
||||||
|
|
||||||
|
val fieldNamesQuery = fieldNames.joinToString { "$it = ?" }
|
||||||
|
val primaryFieldNamesQuery = primaryFieldNames.joinToString(" AND ") { "$it = ?" }
|
||||||
|
val query = "\"\"\"UPDATE $tableName SET $fieldNamesQuery WHERE $primaryFieldNamesQuery\"\"\""
|
||||||
|
|
||||||
|
val entityInsertionAdapter = ClassName("androidx.room", "EntityInsertionAdapter")
|
||||||
|
val supportSQLiteStatement = ClassName("androidx.sqlite.db", "SupportSQLiteStatement")
|
||||||
|
|
||||||
|
val usedTypeConverters = mutableListOf<TypeConverter>()
|
||||||
|
|
||||||
|
val bind = CodeBlock.builder()
|
||||||
|
(fields+primaryFields).forEachIndexed { i, field ->
|
||||||
|
val index = i+1
|
||||||
|
val fieldName = field.simpleName.toString()
|
||||||
|
val name = "${paramName}_$fieldName"
|
||||||
|
val realName = "${paramName}.$fieldName"
|
||||||
|
val nullable = field.getAnnotation(org.jetbrains.annotations.Nullable::class.java) != null
|
||||||
|
|
||||||
|
var param = when (field.asType().kind) {
|
||||||
|
TypeKind.BOOLEAN -> "if ($name) 1L else 0L"
|
||||||
|
TypeKind.BYTE,
|
||||||
|
TypeKind.SHORT,
|
||||||
|
TypeKind.INT -> "$name.toLong()"
|
||||||
|
TypeKind.CHAR -> "$name.toString()"
|
||||||
|
TypeKind.FLOAT -> "$name.toDouble()"
|
||||||
|
else -> when (field.asType().toString()) {
|
||||||
|
"java.lang.String" -> name
|
||||||
|
"java.lang.Boolean" -> "if ($name == true) 1L else 0L"
|
||||||
|
"java.lang.Byte",
|
||||||
|
"java.lang.Short",
|
||||||
|
"java.lang.Integer" -> "$name.toLong()"
|
||||||
|
"java.lang.Long" -> name
|
||||||
|
"java.lang.Char" -> "$name.toString()"
|
||||||
|
"java.lang.Float" -> "$name.toDouble()"
|
||||||
|
"java.lang.Double" -> name
|
||||||
|
else -> name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var isConvert = false
|
||||||
|
val bindMethod = when (field.asType().kind) {
|
||||||
|
TypeKind.BOOLEAN -> "bindLong"
|
||||||
|
TypeKind.BYTE -> "bindLong"
|
||||||
|
TypeKind.SHORT -> "bindLong"
|
||||||
|
TypeKind.INT -> "bindLong"
|
||||||
|
TypeKind.LONG -> "bindLong"
|
||||||
|
TypeKind.CHAR -> "bindString"
|
||||||
|
TypeKind.FLOAT -> "bindDouble"
|
||||||
|
TypeKind.DOUBLE -> "bindDouble"
|
||||||
|
else -> when (field.asType().toString()) {
|
||||||
|
"java.lang.String" -> "bindString"
|
||||||
|
"java.lang.Boolean" -> "bindLong"
|
||||||
|
"java.lang.Byte" -> "bindLong"
|
||||||
|
"java.lang.Short" -> "bindLong"
|
||||||
|
"java.lang.Integer" -> "bindLong"
|
||||||
|
"java.lang.Long" -> "bindLong"
|
||||||
|
"java.lang.Char" -> "bindString"
|
||||||
|
"java.lang.Float" -> "bindDouble"
|
||||||
|
"java.lang.Double" -> "bindDouble"
|
||||||
|
else -> {
|
||||||
|
val converter = typeConverters.firstOrNull {
|
||||||
|
it.dataType.toString() == field.asType().toString()
|
||||||
|
}
|
||||||
|
if (converter != null) {
|
||||||
|
param = "__${converter.converterType.simpleName}.${converter.methodName}($realName)"
|
||||||
|
param = when (converter.returnType.toString()) {
|
||||||
|
"java.lang.Integer", "int",
|
||||||
|
"java.lang.Short", "short",
|
||||||
|
"java.lang.Byte", "byte" -> "$param.toLong()"
|
||||||
|
"java.lang.Boolean", "boolean" -> "if ($param) 1L else 0L"
|
||||||
|
"java.lang.Char", "char" -> "$param.toString()"
|
||||||
|
"java.lang.Float", "float" -> "$param.toDouble()"
|
||||||
|
else -> param
|
||||||
|
}
|
||||||
|
isConvert = true
|
||||||
|
usedTypeConverters += converter
|
||||||
|
when (converter.returnType.toString()) {
|
||||||
|
"java.lang.Integer", "int",
|
||||||
|
"java.lang.Short", "short",
|
||||||
|
"java.lang.Byte", "byte",
|
||||||
|
"java.lang.Boolean", "boolean" -> "bindLong"
|
||||||
|
"java.lang.Char", "char" -> "bindString"
|
||||||
|
"java.lang.Float", "float" -> "bindDouble"
|
||||||
|
else -> "bindString"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else "bind${field.asType()}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isConvert) {
|
||||||
|
bind.addStatement("val $name = $realName")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bind.addStatement("val $name = $param")
|
||||||
|
param = name
|
||||||
|
}
|
||||||
|
if (nullable) {
|
||||||
|
bind.beginControlFlow("if ($name == null)")
|
||||||
|
.addStatement("stmt.bindNull($index)")
|
||||||
|
.endControlFlow()
|
||||||
|
.beginControlFlow("else")
|
||||||
|
.addStatement("stmt.$bindMethod($index, $param)")
|
||||||
|
.endControlFlow()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bind.addStatement("stmt.$bindMethod($index, $param)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val adapterName = "__insertionAdapterOf$methodName"
|
||||||
|
val delegate = CodeBlock.builder().add("""
|
||||||
|
|lazy {
|
||||||
|
| object : EntityInsertionAdapter<%T>(__db) {
|
||||||
|
| override fun createQuery() = $query
|
||||||
|
| override fun bind(stmt: %T, $paramName: %T) {
|
||||||
|
|${bind.indent().indent().indent().build()}
|
||||||
|
| }
|
||||||
|
| }
|
||||||
|
|}""".trimMargin(), paramTypeElement.asClassName(), supportSQLiteStatement, paramTypeElement.asClassName())
|
||||||
|
|
||||||
|
cls.addProperty(PropertySpec.builder(adapterName, entityInsertionAdapter.parameterizedBy(paramTypeElement.asClassName()), KModifier.PRIVATE)
|
||||||
|
.delegate(delegate.build())
|
||||||
|
.build())
|
||||||
|
|
||||||
|
val list = ClassName("kotlin.collections", "List")
|
||||||
|
val longArray = ClassName("kotlin", "LongArray")
|
||||||
|
|
||||||
|
val function = FunSpec.builder(methodName)
|
||||||
|
.addModifiers(KModifier.INTERNAL)
|
||||||
|
.addParameter("item", parameter.asType().asTypeName())
|
||||||
|
.returns(Long::class.java)
|
||||||
|
.addStatement("__db.assertNotSuspendingTransaction()")
|
||||||
|
.addStatement("__db.beginTransaction()")
|
||||||
|
.addCode("""
|
||||||
|
|try {
|
||||||
|
| val _result = $adapterName.insertAndReturnId(item)
|
||||||
|
| __db.setTransactionSuccessful()
|
||||||
|
| return _result
|
||||||
|
|} finally {
|
||||||
|
| __db.endTransaction()
|
||||||
|
|}
|
||||||
|
""".trimMargin())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val functionAll = FunSpec.builder(methodName+"All")
|
||||||
|
.addModifiers(KModifier.INTERNAL)
|
||||||
|
.addParameter("items", list.parameterizedBy(parameter.asType().asTypeName()))
|
||||||
|
.returns(longArray)
|
||||||
|
.addStatement("__db.assertNotSuspendingTransaction()")
|
||||||
|
.addStatement("__db.beginTransaction()")
|
||||||
|
.addCode("""
|
||||||
|
|try {
|
||||||
|
| val _result = $adapterName.insertAndReturnIdsArray(items)
|
||||||
|
| __db.setTransactionSuccessful()
|
||||||
|
| return _result
|
||||||
|
|} finally {
|
||||||
|
| __db.endTransaction()
|
||||||
|
|}
|
||||||
|
""".trimMargin())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
cls.addFunction(function)
|
||||||
|
cls.addFunction(functionAll)
|
||||||
|
return usedTypeConverters
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSupportedAnnotationTypes(): MutableSet<String> {
|
||||||
|
return mutableSetOf(SelectiveDao::class.java.canonicalName)
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
include ':codegen'
|
||||||
|
include ':annotation'
|
||||||
rootProject.name='Szkolny.eu'
|
rootProject.name='Szkolny.eu'
|
||||||
include ':app', ':agendacalendarview', ':mhttp', ':material-about-library', ':cafebar', ':szkolny-font', ':nachos'
|
include ':app', ':agendacalendarview', ':mhttp', ':material-about-library', ':cafebar', ':szkolny-font', ':nachos'
|
||||||
/*
|
/*
|
||||||
|
Reference in New Issue
Block a user