[UI] Migrate to base fragment classes, restructure base modules (#206)
Some checks failed
Schedule/dispatch / Build nightly release (APK) (push) Has been skipped
Schedule/dispatch / Check new changes (push) Failing after 20s

* Replace android:visibility with android:isVisible

* Move base dialogs and views to .ui.base

* Move uncategorized classes to ui.main

* Fix view classes in XML

* Add BaseFragment, use for LazyFragment

* Migrate fragments to BaseFragment

* Move activity setup from BaseFragment, recreate Job on attach

* Move BaseFragment initialization to onResume

* Add base PagerFragment, add TemplateFragment to drawer

* Support swipe-to-refresh in base fragments

* Remove SwipeRefreshLayout split implementation

* Rename onViewCreated to onViewReady

* Remove LazyFragment

* Save page selection in PagerFragment, migrate AttendanceFragment

* Migrate fragments to PagerFragment

* Migrate contributors to a fragment

* Migrate TimetableFragment to PagerFragment

* Fix disabling SwipeToRefresh on page scroll

* Fix crash in CrashActivity
This commit is contained in:
Kuba Szczodrzyński 2024-07-04 22:14:52 +02:00 committed by GitHub
parent f795412551
commit 10043cc62c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
154 changed files with 1919 additions and 3171 deletions

View File

@ -143,6 +143,7 @@
</codeStyleSettings> </codeStyleSettings>
<codeStyleSettings language="kotlin"> <codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" />
<option name="PARAMETER_ANNOTATION_WRAP" value="2" /> <option name="PARAMETER_ANNOTATION_WRAP" value="2" />
<option name="ENUM_CONSTANTS_WRAP" value="2" /> <option name="ENUM_CONSTANTS_WRAP" value="2" />
</codeStyleSettings> </codeStyleSettings>

View File

@ -126,7 +126,7 @@
/ ____ \ (__| |_| |\ V /| | |_| | __/\__ \ / ____ \ (__| |_| |\ V /| | |_| | __/\__ \
/_/ \_\___|\__|_| \_/ |_|\__|_|\___||___/ /_/ \_\___|\__|_| \_/ |_|\__|_|\___||___/
--> -->
<activity android:name=".ui.base.CrashActivity" <activity android:name=".ui.main.CrashActivity"
android:configChanges="orientation|screenSize|keyboardHidden" android:configChanges="orientation|screenSize|keyboardHidden"
android:process=":error_activity" android:process=":error_activity"
android:exported="false" android:exported="false"
@ -163,8 +163,7 @@
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"
android:exported="false" android:exported="false"
android:theme="@style/Theme.MaterialComponents.Light.DarkActionBar" /> android:theme="@style/Theme.MaterialComponents.Light.DarkActionBar" />
<activity android:name=".ui.base.BuildInvalidActivity" android:exported="false" /> <activity android:name=".ui.main.BuildInvalidActivity" android:exported="false" />
<activity android:name=".ui.settings.contributors.ContributorsActivity" android:exported="false" />
<!-- _____ _ <!-- _____ _
| __ \ (_) | __ \ (_)

View File

@ -54,7 +54,7 @@ import pl.szczodrzynski.edziennik.data.db.AppDb
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.enums.LoginType import pl.szczodrzynski.edziennik.data.enums.LoginType
import pl.szczodrzynski.edziennik.network.SSLProviderInstaller import pl.szczodrzynski.edziennik.network.SSLProviderInstaller
import pl.szczodrzynski.edziennik.ui.base.CrashActivity import pl.szczodrzynski.edziennik.ui.main.CrashActivity
import pl.szczodrzynski.edziennik.utils.PermissionChecker import pl.szczodrzynski.edziennik.utils.PermissionChecker
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
import timber.log.Timber import timber.log.Timber

View File

@ -19,6 +19,7 @@ import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils import androidx.core.graphics.ColorUtils
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.navigation.NavOptions import androidx.navigation.NavOptions
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.danimahardhika.cafebar.CafeBar import com.danimahardhika.cafebar.CafeBar
import com.danimahardhika.cafebar.CafeBarTheme import com.danimahardhika.cafebar.CafeBarTheme
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
@ -49,7 +50,7 @@ import pl.szczodrzynski.edziennik.core.work.AppManagerDetectedEvent
import pl.szczodrzynski.edziennik.core.work.SyncWorker import pl.szczodrzynski.edziennik.core.work.SyncWorker
import pl.szczodrzynski.edziennik.core.work.UpdateStateEvent import pl.szczodrzynski.edziennik.core.work.UpdateStateEvent
import pl.szczodrzynski.edziennik.core.work.UpdateWorker import pl.szczodrzynski.edziennik.core.work.UpdateWorker
import pl.szczodrzynski.edziennik.ui.base.MainSnackbar import pl.szczodrzynski.edziennik.ui.main.MainSnackbar
import pl.szczodrzynski.edziennik.data.enums.NavTarget import pl.szczodrzynski.edziennik.data.enums.NavTarget
import pl.szczodrzynski.edziennik.data.enums.NavTargetLocation import pl.szczodrzynski.edziennik.data.enums.NavTargetLocation
import pl.szczodrzynski.edziennik.ui.dialogs.ChangelogDialog import pl.szczodrzynski.edziennik.ui.dialogs.ChangelogDialog
@ -59,8 +60,8 @@ import pl.szczodrzynski.edziennik.ui.dialogs.sync.ServerMessageDialog
import pl.szczodrzynski.edziennik.ui.dialogs.sync.SyncViewListDialog import pl.szczodrzynski.edziennik.ui.dialogs.sync.SyncViewListDialog
import pl.szczodrzynski.edziennik.ui.dialogs.sync.UpdateAvailableDialog import pl.szczodrzynski.edziennik.ui.dialogs.sync.UpdateAvailableDialog
import pl.szczodrzynski.edziennik.ui.dialogs.sync.UpdateProgressDialog import pl.szczodrzynski.edziennik.ui.dialogs.sync.UpdateProgressDialog
import pl.szczodrzynski.edziennik.ui.error.ErrorDetailsDialog import pl.szczodrzynski.edziennik.ui.dialogs.ErrorDetailsDialog
import pl.szczodrzynski.edziennik.ui.error.ErrorSnackbar import pl.szczodrzynski.edziennik.ui.main.ErrorSnackbar
import pl.szczodrzynski.edziennik.ui.event.EventManualDialog import pl.szczodrzynski.edziennik.ui.event.EventManualDialog
import pl.szczodrzynski.edziennik.ui.login.LoginActivity import pl.szczodrzynski.edziennik.ui.login.LoginActivity
import pl.szczodrzynski.edziennik.ui.messages.list.MessagesFragment import pl.szczodrzynski.edziennik.ui.messages.list.MessagesFragment
@ -76,7 +77,6 @@ import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
import pl.szczodrzynski.navlib.drawer.NavDrawer import pl.szczodrzynski.navlib.drawer.NavDrawer
import pl.szczodrzynski.navlib.drawer.items.DrawerPrimaryItem import pl.szczodrzynski.navlib.drawer.items.DrawerPrimaryItem
import timber.log.Timber import timber.log.Timber
import java.io.IOException
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -97,7 +97,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
val errorSnackbar: ErrorSnackbar by lazy { ErrorSnackbar(this) } val errorSnackbar: ErrorSnackbar by lazy { ErrorSnackbar(this) }
val requestHandler by lazy { MainActivityRequestHandler(this) } val requestHandler by lazy { MainActivityRequestHandler(this) }
val swipeRefreshLayout: SwipeRefreshLayoutNoTouch by lazy { b.swipeRefreshLayout } val swipeRefreshLayout: SwipeRefreshLayout by lazy { b.swipeRefreshLayout }
var onBeforeNavigate: (() -> Boolean)? = null var onBeforeNavigate: (() -> Boolean)? = null
private var pausedNavigationData: PausedNavigationData? = null private var pausedNavigationData: PausedNavigationData? = null
@ -264,9 +264,8 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
drawer.setUnreadCounterList(unreadCounters) drawer.setUnreadCounterList(unreadCounters)
} }
b.swipeRefreshLayout.isEnabled = true swipeRefreshLayout.setOnRefreshListener { launch { syncCurrentFeature() } }
b.swipeRefreshLayout.setOnRefreshListener { launch { syncCurrentFeature() } } swipeRefreshLayout.setColorSchemeResources(
b.swipeRefreshLayout.setColorSchemeResources(
R.color.md_blue_500, R.color.md_blue_500,
R.color.md_amber_500, R.color.md_amber_500,
R.color.md_green_500 R.color.md_green_500
@ -939,6 +938,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
val arguments = args val arguments = args
?: navBackStack.firstOrNull { it.first == navTarget }?.second ?: navBackStack.firstOrNull { it.first == navTarget }?.second
?: Bundle() ?: Bundle()
swipeRefreshLayout.isEnabled = false
bottomSheet.close() bottomSheet.close()
bottomSheet.removeAllContextual() bottomSheet.removeAllContextual()
bottomSheet.toggleGroupEnabled = false bottomSheet.toggleGroupEnabled = false

View File

@ -34,7 +34,7 @@ import pl.szczodrzynski.edziennik.ext.md5
import pl.szczodrzynski.edziennik.ext.resolveAttr import pl.szczodrzynski.edziennik.ext.resolveAttr
import pl.szczodrzynski.edziennik.ext.resolveColor import pl.szczodrzynski.edziennik.ext.resolveColor
import pl.szczodrzynski.edziennik.ext.toJsonObject import pl.szczodrzynski.edziennik.ext.toJsonObject
import pl.szczodrzynski.edziennik.ui.base.BuildInvalidActivity import pl.szczodrzynski.edziennik.ui.main.BuildInvalidActivity
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
import timber.log.Timber import timber.log.Timber
import java.time.Instant import java.time.Instant

View File

@ -69,6 +69,14 @@ class FirebaseManager(val app: App) {
) )
fun initializeApps() { fun initializeApps() {
try {
// skip Firebase setup if the default app is not initialized
// (e.g. in the crash process)
FirebaseApp.getInstance()
} catch (e: IllegalStateException) {
return
}
FirebaseInstanceId.getInstance().instanceId.addOnSuccessListener { result -> FirebaseInstanceId.getInstance().instanceId.addOnSuccessListener { result ->
val token = result.token val token = result.token
Timber.i("Got App token: $token") Timber.i("Got App token: $token")

View File

@ -28,8 +28,8 @@ import pl.szczodrzynski.edziennik.ext.keys
import pl.szczodrzynski.edziennik.ext.md5 import pl.szczodrzynski.edziennik.ext.md5
import pl.szczodrzynski.edziennik.ext.toApiError import pl.szczodrzynski.edziennik.ext.toApiError
import pl.szczodrzynski.edziennik.ext.toErrorCode import pl.szczodrzynski.edziennik.ext.toErrorCode
import pl.szczodrzynski.edziennik.ui.error.ErrorDetailsDialog import pl.szczodrzynski.edziennik.ui.dialogs.ErrorDetailsDialog
import pl.szczodrzynski.edziennik.ui.error.ErrorSnackbar import pl.szczodrzynski.edziennik.ui.main.ErrorSnackbar
import pl.szczodrzynski.edziennik.ui.login.LoginInfo import pl.szczodrzynski.edziennik.ui.login.LoginInfo
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time

View File

@ -30,7 +30,9 @@ import pl.szczodrzynski.edziennik.ui.notes.NotesFragment
import pl.szczodrzynski.edziennik.ui.notifications.NotificationsListFragment import pl.szczodrzynski.edziennik.ui.notifications.NotificationsListFragment
import pl.szczodrzynski.edziennik.ui.settings.ProfileManagerFragment import pl.szczodrzynski.edziennik.ui.settings.ProfileManagerFragment
import pl.szczodrzynski.edziennik.ui.settings.SettingsFragment import pl.szczodrzynski.edziennik.ui.settings.SettingsFragment
import pl.szczodrzynski.edziennik.ui.settings.contributors.ContributorsFragment
import pl.szczodrzynski.edziennik.ui.teachers.TeachersListFragment import pl.szczodrzynski.edziennik.ui.teachers.TeachersListFragment
import pl.szczodrzynski.edziennik.ui.template.TemplateFragment
import pl.szczodrzynski.edziennik.ui.timetable.TimetableFragment import pl.szczodrzynski.edziennik.ui.timetable.TimetableFragment
import pl.szczodrzynski.edziennik.ui.webpush.WebPushFragment import pl.szczodrzynski.edziennik.ui.webpush.WebPushFragment
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
@ -177,6 +179,15 @@ enum class NavTarget(
popTo = HOME, popTo = HOME,
devModeOnly = true, devModeOnly = true,
), ),
TEMPLATE(
id = 1001,
fragmentClass = TemplateFragment::class.java,
location = NavTargetLocation.DRAWER_BOTTOM,
nameRes = R.string.menu_template,
icon = CommunityMaterial.Icon.cmd_code_braces,
popTo = HOME,
devModeOnly = true,
),
PROFILE_ADD( PROFILE_ADD(
id = 200, id = 200,
fragmentClass = null, fragmentClass = null,
@ -243,6 +254,11 @@ enum class NavTarget(
id = 140, id = 140,
fragmentClass = WebPushFragment::class.java, fragmentClass = WebPushFragment::class.java,
nameRes = R.string.menu_web_push, nameRes = R.string.menu_web_push,
),
CONTRIBUTORS(
id = 150,
fragmentClass = ContributorsFragment::class.java,
nameRes = R.string.contributors,
); );
companion object { companion object {

View File

@ -31,34 +31,33 @@ fun Bundle?.getIntOrNull(key: String): Int? {
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun <T : Any> Bundle?.get(key: String): T? { operator fun Bundle.set(key: String, value: Any) = when (value) {
return this?.get(key) as? T? is String -> putString(key, value as String?)
is Char -> putChar(key, value)
is Int -> putInt(key, value)
is Long -> putLong(key, value)
is Float -> putFloat(key, value)
is Short -> putShort(key, value)
is Double -> putDouble(key, value)
is Boolean -> putBoolean(key, value)
is Bundle -> putBundle(key, value)
is Enum<*> -> putEnum(key, value)
is Array<*> -> putParcelableArray(key, value as Array<out Parcelable>)
is Parcelable -> putParcelable(key, value)
is Serializable -> putSerializable(key, value)
else -> throw IllegalArgumentException("Couldn't serialize $key = $value")
} }
@Suppress("UNCHECKED_CAST") fun Bundle.putExtras(vararg properties: Pair<String, Any?>): Bundle {
fun Bundle(vararg properties: Pair<String, Any?>): Bundle { for (property in properties) {
return Bundle().apply { val (key, value) = property
for (property in properties) { this[key] = value ?: continue
val (key, value) = property
when (value) {
is String -> putString(key, value as String?)
is Char -> putChar(key, value)
is Int -> putInt(key, value)
is Long -> putLong(key, value)
is Float -> putFloat(key, value)
is Short -> putShort(key, value)
is Double -> putDouble(key, value)
is Boolean -> putBoolean(key, value)
is Bundle -> putBundle(key, value)
is Enum<*> -> putEnum(key, value)
is Array<*> -> putParcelableArray(key, value as Array<out Parcelable>)
is Parcelable -> putParcelable(key, value)
is Serializable -> putSerializable(key, value)
}
}
} }
return this
} }
fun Bundle(vararg properties: Pair<String, Any?>) = Bundle().putExtras(*properties)
fun Intent(action: String? = null, vararg properties: Pair<String, Any?>): Intent { fun Intent(action: String? = null, vararg properties: Pair<String, Any?>): Intent {
return Intent(action).putExtras(Bundle(*properties)) return Intent(action).putExtras(Bundle(*properties))
} }
@ -68,7 +67,7 @@ fun Intent(packageContext: Context, cls: Class<*>, vararg properties: Pair<Strin
} }
fun Intent.putExtras(vararg properties: Pair<String, Any?>) = putExtras(Bundle(*properties)) fun Intent.putExtras(vararg properties: Pair<String, Any?>) = putExtras(Bundle(*properties))
fun Bundle.putExtras(vararg properties: Pair<String, Any?>) = putAll(Bundle(*properties))
fun Bundle.toJsonObject(): JsonObject { fun Bundle.toJsonObject(): JsonObject {
val json = JsonObject() val json = JsonObject()

View File

@ -7,10 +7,11 @@ package pl.szczodrzynski.edziennik.ext
import android.app.UiModeManager import android.app.UiModeManager
import android.content.Context import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
val Context.app val AppCompatActivity.app
get() = applicationContext as App get() = applicationContext as App
val Context.isNightMode: Boolean val Context.isNightMode: Boolean

View File

@ -61,6 +61,12 @@ fun @receiver:AttrRes Int.resolveAttr(context: Context?): Int {
} }
return typedValue.data return typedValue.data
} }
@StyleRes
fun @receiver:AttrRes Int.resolveStyleAttr(context: Context?): Int {
val typedValue = TypedValue()
context?.theme?.resolveAttribute(this, typedValue, true)
return typedValue.data
}
@Dimension @Dimension
fun @receiver:AttrRes Int.resolveDimenAttr(context: Context): Float { fun @receiver:AttrRes Int.resolveDimenAttr(context: Context): Float {
val typedValue = TypedValue() val typedValue = TypedValue()

View File

@ -13,7 +13,6 @@ import android.view.WindowManager
import android.widget.* import android.widget.*
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
@ -152,16 +151,6 @@ inline fun ViewPager.addOnPageSelectedListener(crossinline block: (position: Int
override fun onPageSelected(position: Int) { block(position) } 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
}
}
fun View.removeFromParent() { fun View.removeFromParent() {
(parent as? ViewGroup)?.removeView(this) (parent as? ViewGroup)?.removeView(this)
} }

View File

@ -8,135 +8,103 @@ 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 android.widget.Toast
import androidx.databinding.ViewDataBinding import androidx.databinding.ViewDataBinding
import androidx.fragment.app.Fragment
import com.applandeo.materialcalendarview.EventDay import com.applandeo.materialcalendarview.EventDay
import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp import com.mikepenz.iconics.utils.sizeDp
import eu.szkolny.font.SzkolnyFont import eu.szkolny.font.SzkolnyFont
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.EventType
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.enums.MetadataType import pl.szczodrzynski.edziennik.data.enums.MetadataType
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaCalendarBinding import pl.szczodrzynski.edziennik.databinding.FragmentAgendaCalendarBinding
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaDefaultBinding import pl.szczodrzynski.edziennik.databinding.FragmentAgendaDefaultBinding
import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
import pl.szczodrzynski.edziennik.ui.dialogs.settings.AgendaConfigDialog import pl.szczodrzynski.edziennik.ui.dialogs.settings.AgendaConfigDialog
import pl.szczodrzynski.edziennik.ui.event.EventManualDialog import pl.szczodrzynski.edziennik.ui.event.EventManualDialog
import pl.szczodrzynski.edziennik.utils.Themes
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
import kotlin.coroutines.CoroutineContext
class AgendaFragment : Fragment(), CoroutineScope { class AgendaFragment : BaseFragment<ViewDataBinding, MainActivity>(
inflater = null,
) {
private lateinit var activity: MainActivity override fun inflate(
private lateinit var b: ViewDataBinding inflater: LayoutInflater,
parent: ViewGroup?,
attachToParent: Boolean,
) = when (app.profile.config.ui.agendaViewType) {
Profile.AGENDA_DEFAULT -> FragmentAgendaDefaultBinding.inflate(inflater, parent, false)
Profile.AGENDA_CALENDAR -> FragmentAgendaCalendarBinding.inflate(inflater, parent, false)
else -> null
}
private val app by lazy { activity.app } override fun getFab() = R.string.add to CommunityMaterial.Icon3.cmd_plus
override fun getMarkAsReadType() = MetadataType.EVENT
private var job = Job() override fun getBottomSheetItems() = listOf(
override val coroutineContext: CoroutineContext BottomSheetPrimaryItem(true)
get() = job + Dispatchers.Main .withTitle(R.string.menu_add_event)
.withDescription(R.string.menu_add_event_desc)
private var type: Int = Profile.AGENDA_DEFAULT .withIcon(SzkolnyFont.Icon.szf_calendar_plus_outline)
.withOnClickListener {
activity.bottomSheet.close()
EventManualDialog(
activity,
app.profileId,
defaultDate = AgendaFragmentDefault.selectedDate
).show()
},
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_agenda_config)
.withIcon(CommunityMaterial.Icon.cmd_cog_outline)
.withOnClickListener {
activity.bottomSheet.close()
AgendaConfigDialog(activity, true, null, null).show()
},
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_agenda_change_view)
.withIcon(
if (app.profile.config.ui.agendaViewType == Profile.AGENDA_DEFAULT)
CommunityMaterial.Icon.cmd_calendar_outline
else
CommunityMaterial.Icon2.cmd_format_list_bulleted_square
)
.withOnClickListener {
activity.bottomSheet.close()
app.profile.config.ui.agendaViewType =
if (app.profile.config.ui.agendaViewType == Profile.AGENDA_DEFAULT)
Profile.AGENDA_CALENDAR
else
Profile.AGENDA_DEFAULT
activity.reloadTarget()
},
)
private var agendaDefault: AgendaFragmentDefault? = null private var agendaDefault: AgendaFragmentDefault? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override suspend fun onViewReady(savedInstanceState: Bundle?) {
if (getActivity() == null || context == null) return null when (app.profile.config.ui.agendaViewType) {
activity = getActivity() as MainActivity Profile.AGENDA_DEFAULT -> createDefaultAgendaView(
type = app.profile.config.ui.agendaViewType b as? FragmentAgendaDefaultBinding ?: return
b = when (type) { )
Profile.AGENDA_DEFAULT -> FragmentAgendaDefaultBinding.inflate(inflater, container, false) Profile.AGENDA_CALENDAR -> createCalendarAgendaView(
Profile.AGENDA_CALENDAR -> FragmentAgendaCalendarBinding.inflate(inflater, container, false) b as? FragmentAgendaCalendarBinding ?: return
else -> return null )
} }
return b.root
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override suspend fun onFabClick() {
if (!isAdded) return EventManualDialog(
activity,
activity.bottomSheet.prependItems( app.profileId,
BottomSheetPrimaryItem(true) defaultDate = AgendaFragmentDefault.selectedDate
.withTitle(R.string.menu_add_event) ).show()
.withDescription(R.string.menu_add_event_desc)
.withIcon(SzkolnyFont.Icon.szf_calendar_plus_outline)
.withOnClickListener {
activity.bottomSheet.close()
EventManualDialog(
activity,
app.profileId,
defaultDate = AgendaFragmentDefault.selectedDate
).show()
},
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_agenda_config)
.withIcon(CommunityMaterial.Icon.cmd_cog_outline)
.withOnClickListener {
activity.bottomSheet.close()
AgendaConfigDialog(activity, true, null, null).show()
},
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_agenda_change_view)
.withIcon(if (type == Profile.AGENDA_DEFAULT) CommunityMaterial.Icon.cmd_calendar_outline else CommunityMaterial.Icon2.cmd_format_list_bulleted_square)
.withOnClickListener {
activity.bottomSheet.close()
type =
if (type == Profile.AGENDA_DEFAULT) Profile.AGENDA_CALENDAR else Profile.AGENDA_DEFAULT
app.profile.config.ui.agendaViewType = type
activity.reloadTarget()
},
BottomSheetSeparatorItem(true),
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_mark_as_read)
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
.withOnClickListener {
launch {
activity.bottomSheet.close()
withContext(Dispatchers.Default) {
App.db.metadataDao()
.setAllSeen(app.profileId, MetadataType.EVENT, true)
}
Toast.makeText(
activity,
R.string.main_menu_mark_as_read_success,
Toast.LENGTH_SHORT
).show()
}
}
)
activity.navView.bottomBar.fabEnable = true
activity.navView.bottomBar.fabExtendedText = getString(R.string.add)
activity.navView.bottomBar.fabIcon = CommunityMaterial.Icon3.cmd_plus
activity.navView.setFabOnClickListener {
EventManualDialog(
activity,
app.profileId,
defaultDate = AgendaFragmentDefault.selectedDate
).show()
}
activity.gainAttention()
activity.gainAttentionFAB()
when (type) {
Profile.AGENDA_DEFAULT -> createDefaultAgendaView()
Profile.AGENDA_CALENDAR -> createCalendarAgendaView()
}
} }
private suspend fun checkEventTypes() { private suspend fun checkEventTypes() {
@ -145,23 +113,25 @@ class AgendaFragment : Fragment(), CoroutineScope {
} }
} }
private fun createDefaultAgendaView() { (b as? FragmentAgendaDefaultBinding)?.let { b -> launch { private suspend fun createDefaultAgendaView(b: FragmentAgendaDefaultBinding) {
if (!isAdded) if (!isAdded)
return@launch return
checkEventTypes() checkEventTypes()
delay(500) delay(500)
agendaDefault = AgendaFragmentDefault(activity, app, b) agendaDefault = AgendaFragmentDefault(activity, app, b)
agendaDefault?.initView(this@AgendaFragment) agendaDefault?.initView(this@AgendaFragment)
}}} }
private fun createCalendarAgendaView() { (b as? FragmentAgendaCalendarBinding)?.let { b -> launch { private suspend fun createCalendarAgendaView(b: FragmentAgendaCalendarBinding) {
checkEventTypes() checkEventTypes()
delay(300) delay(300)
val dayList = mutableListOf<EventDay>() val dayList = mutableListOf<EventDay>()
val events = withContext(Dispatchers.Default) { app.db.eventDao().getAllNow(app.profileId) } val events = withContext(Dispatchers.IO) {
app.db.eventDao().getAllNow(app.profileId)
}
val unreadEventDates = mutableSetOf<Int>() val unreadEventDates = mutableSetOf<Int>()
events.forEach { event -> events.forEach { event ->
@ -189,5 +159,5 @@ class AgendaFragment : Fragment(), CoroutineScope {
}} }}
b.progressBar.visibility = View.GONE b.progressBar.visibility = View.GONE
}}} }
} }

View File

@ -22,7 +22,7 @@ import pl.szczodrzynski.edziennik.ui.agenda.lessonchanges.LessonChangesEventRend
import pl.szczodrzynski.edziennik.ui.agenda.teacherabsence.TeacherAbsenceDialog import pl.szczodrzynski.edziennik.ui.agenda.teacherabsence.TeacherAbsenceDialog
import pl.szczodrzynski.edziennik.ui.agenda.teacherabsence.TeacherAbsenceEvent import pl.szczodrzynski.edziennik.ui.agenda.teacherabsence.TeacherAbsenceEvent
import pl.szczodrzynski.edziennik.ui.agenda.teacherabsence.TeacherAbsenceEventRenderer import pl.szczodrzynski.edziennik.ui.agenda.teacherabsence.TeacherAbsenceEventRenderer
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BindingDialog
import pl.szczodrzynski.edziennik.ui.event.EventDetailsDialog import pl.szczodrzynski.edziennik.ui.event.EventDetailsDialog
import pl.szczodrzynski.edziennik.ui.event.EventListAdapter import pl.szczodrzynski.edziennik.ui.event.EventListAdapter
import pl.szczodrzynski.edziennik.ui.event.EventManualDialog import pl.szczodrzynski.edziennik.ui.event.EventManualDialog

View File

@ -7,7 +7,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.DialogLessonChangeListBinding import pl.szczodrzynski.edziennik.databinding.DialogLessonChangeListBinding
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BindingDialog
import pl.szczodrzynski.edziennik.ui.timetable.LessonDetailsDialog import pl.szczodrzynski.edziennik.ui.timetable.LessonDetailsDialog
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date

View File

@ -7,7 +7,7 @@ import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.DialogTeacherAbsenceListBinding import pl.szczodrzynski.edziennik.databinding.DialogTeacherAbsenceListBinding
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BindingDialog
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class TeacherAbsenceDialog( class TeacherAbsenceDialog(

View File

@ -51,7 +51,6 @@ public class AnnouncementsFragment extends Fragment {
app = (App) activity.getApplication(); app = (App) activity.getApplication();
// activity, context and profile is valid // activity, context and profile is valid
b = DataBindingUtil.inflate(inflater, R.layout.fragment_announcements, container, false); b = DataBindingUtil.inflate(inflater, R.layout.fragment_announcements, container, false);
b.refreshLayout.setParent(activity.getSwipeRefreshLayout());
return b.getRoot(); return b.getRoot();
} }
@ -77,10 +76,6 @@ public class AnnouncementsFragment extends Fragment {
}) })
); );
/*b.refreshLayout.setOnRefreshListener(() -> {
activity.syncCurrentFeature(MainActivity.DRAWER_ITEM_ANNOUNCEMENTS, b.refreshLayout);
});*/
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext()); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
//RecyclerViewExpandableItemManager expMgr = new RecyclerViewExpandableItemManager(null); //RecyclerViewExpandableItemManager expMgr = new RecyclerViewExpandableItemManager(null);
@ -90,18 +85,6 @@ public class AnnouncementsFragment extends Fragment {
recyclerView.setLayoutManager(linearLayoutManager); recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.addItemDecoration(new SimpleDividerItemDecoration(view.getContext())); recyclerView.addItemDecoration(new SimpleDividerItemDecoration(view.getContext()));
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
if (recyclerView.canScrollVertically(-1)) {
b.refreshLayout.setEnabled(false);
}
if (!recyclerView.canScrollVertically(-1) && newState == SCROLL_STATE_IDLE) {
b.refreshLayout.setEnabled(true);
}
}
});
app.getDb().announcementDao().getAll(App.Companion.getProfileId()).observe(getViewLifecycleOwner(), announcements -> { app.getDb().announcementDao().getAll(App.Companion.getProfileId()).observe(getViewLifecycleOwner(), announcements -> {
if (app == null || activity == null || b == null || !isAdded()) if (app == null || activity == null || b == null || !isAdded())
return; return;

View File

@ -13,7 +13,7 @@ import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
import pl.szczodrzynski.edziennik.databinding.AttendanceDetailsDialogBinding import pl.szczodrzynski.edziennik.databinding.AttendanceDetailsDialogBinding
import pl.szczodrzynski.edziennik.ext.setTintColor import pl.szczodrzynski.edziennik.ext.setTintColor
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BindingDialog
import pl.szczodrzynski.edziennik.ui.notes.setupNotesButton import pl.szczodrzynski.edziennik.ui.notes.setupNotesButton
import pl.szczodrzynski.edziennik.utils.BetterLink import pl.szczodrzynski.edziennik.utils.BetterLink
import pl.szczodrzynski.edziennik.core.manager.NoteManager import pl.szczodrzynski.edziennik.core.manager.NoteManager

View File

@ -4,118 +4,59 @@
package pl.szczodrzynski.edziennik.ui.attendance package pl.szczodrzynski.edziennik.ui.attendance
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 com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial 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.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.enums.MetadataType import pl.szczodrzynski.edziennik.data.enums.MetadataType
import pl.szczodrzynski.edziennik.databinding.AttendanceFragmentBinding import pl.szczodrzynski.edziennik.databinding.BasePagerFragmentBinding
import pl.szczodrzynski.edziennik.ext.Bundle import pl.szczodrzynski.edziennik.ext.Bundle
import pl.szczodrzynski.edziennik.ext.addOnPageSelectedListener import pl.szczodrzynski.edziennik.ui.base.fragment.PagerFragment
import pl.szczodrzynski.edziennik.ui.base.lazypager.FragmentLazyPagerAdapter
import pl.szczodrzynski.edziennik.ui.dialogs.settings.AttendanceConfigDialog import pl.szczodrzynski.edziennik.ui.dialogs.settings.AttendanceConfigDialog
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
import kotlin.coroutines.CoroutineContext
class AttendanceFragment : Fragment(), CoroutineScope { class AttendanceFragment : PagerFragment<BasePagerFragmentBinding, MainActivity>(
inflater = BasePagerFragmentBinding::inflate,
) {
companion object { companion object {
private const val TAG = "AttendanceFragment"
const val VIEW_SUMMARY = 0 const val VIEW_SUMMARY = 0
const val VIEW_DAYS = 1 const val VIEW_DAYS = 1
const val VIEW_MONTHS = 2 const val VIEW_MONTHS = 2
const val VIEW_TYPES = 3 const val VIEW_TYPES = 3
const val VIEW_LIST = 4 const val VIEW_LIST = 4
var pageSelection = 1
} }
private lateinit var app: App override var savedPageSelection
private lateinit var activity: MainActivity get() = app.profile.config.attendance.attendancePageSelection
private lateinit var b: AttendanceFragmentBinding set(value) {
app.profile.config.attendance.attendancePageSelection = value
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 = AttendanceFragmentBinding.inflate(inflater)
b.refreshLayout.setParent(activity.swipeRefreshLayout)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (!isAdded) return
activity.bottomSheet.prependItems(
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_attendance_config)
.withIcon(CommunityMaterial.Icon.cmd_cog_outline)
.withOnClickListener(View.OnClickListener {
activity.bottomSheet.close()
AttendanceConfigDialog(activity, true, null, null).show()
}),
BottomSheetSeparatorItem(true),
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_mark_as_read)
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
.withOnClickListener(View.OnClickListener {
activity.bottomSheet.close()
AsyncTask.execute { App.db.metadataDao().setAllSeen(App.profileId, MetadataType.ATTENDANCE, true) }
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show()
})
)
activity.gainAttention()
if (pageSelection == 1)
pageSelection = app.profile.config.attendance.attendancePageSelection
val pagerAdapter = FragmentLazyPagerAdapter(
parentFragmentManager,
b.refreshLayout,
listOf(
AttendanceSummaryFragment() to getString(R.string.attendance_tab_summary),
AttendanceListFragment().apply {
arguments = Bundle("viewType" to VIEW_DAYS)
} to getString(R.string.attendance_tab_days),
AttendanceListFragment().apply {
arguments = Bundle("viewType" to VIEW_MONTHS)
} to getString(R.string.attendance_tab_months),
AttendanceListFragment().apply {
arguments = Bundle("viewType" to VIEW_TYPES)
} to getString(R.string.attendance_tab_types),
AttendanceListFragment().apply {
arguments = Bundle("viewType" to VIEW_LIST)
} to getString(R.string.attendance_tab_list)
)
)
b.viewPager.apply {
offscreenPageLimit = 1
adapter = pagerAdapter
currentItem = pageSelection
addOnPageSelectedListener {
pageSelection = it
app.profile.config.attendance.attendancePageSelection = it
}
b.tabLayout.setupWithViewPager(this)
} }
}
override fun getMarkAsReadType() = MetadataType.ATTENDANCE
override fun getBottomSheetItems() = listOf(
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_attendance_config)
.withIcon(CommunityMaterial.Icon.cmd_cog_outline)
.withOnClickListener {
activity.bottomSheet.close()
AttendanceConfigDialog(activity, true, null, null).show()
},
)
override fun getTabLayout() = b.tabLayout
override fun getViewPager() = b.viewPager
override suspend fun onCreatePages() = listOf(
AttendanceSummaryFragment() to getString(R.string.attendance_tab_summary),
AttendanceListFragment().apply {
arguments = Bundle("viewType" to VIEW_DAYS)
} to getString(R.string.attendance_tab_days),
AttendanceListFragment().apply {
arguments = Bundle("viewType" to VIEW_MONTHS)
} to getString(R.string.attendance_tab_months),
AttendanceListFragment().apply {
arguments = Bundle("viewType" to VIEW_TYPES)
} to getString(R.string.attendance_tab_types),
AttendanceListFragment().apply {
arguments = Bundle("viewType" to VIEW_LIST)
} to getString(R.string.attendance_tab_list)
)
} }

View File

@ -5,13 +5,12 @@
package pl.szczodrzynski.edziennik.ui.attendance package pl.szczodrzynski.edziennik.ui.attendance
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.coroutines.* import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.data.db.entity.Attendance import pl.szczodrzynski.edziennik.data.db.entity.Attendance
@ -22,41 +21,20 @@ import pl.szczodrzynski.edziennik.ext.startCoroutineTimer
import pl.szczodrzynski.edziennik.ui.attendance.models.AttendanceDayRange import pl.szczodrzynski.edziennik.ui.attendance.models.AttendanceDayRange
import pl.szczodrzynski.edziennik.ui.attendance.models.AttendanceMonth import pl.szczodrzynski.edziennik.ui.attendance.models.AttendanceMonth
import pl.szczodrzynski.edziennik.ui.attendance.models.AttendanceTypeGroup import pl.szczodrzynski.edziennik.ui.attendance.models.AttendanceTypeGroup
import pl.szczodrzynski.edziennik.ui.base.lazypager.LazyFragment import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
import pl.szczodrzynski.edziennik.ui.grades.models.GradesSubject import pl.szczodrzynski.edziennik.ui.grades.models.GradesSubject
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import kotlin.coroutines.CoroutineContext
class AttendanceListFragment : LazyFragment(), CoroutineScope { class AttendanceListFragment : BaseFragment<AttendanceListFragmentBinding, MainActivity>(
companion object { inflater = AttendanceListFragmentBinding::inflate,
private const val TAG = "AttendanceListFragment" ) {
}
private lateinit var app: App override fun getRefreshScrollingView() = b.list
private lateinit var activity: MainActivity
private lateinit var b: AttendanceListFragmentBinding
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local/private variables go here
private val manager
get() = app.attendanceManager
private var viewType = AttendanceFragment.VIEW_DAYS private var viewType = AttendanceFragment.VIEW_DAYS
private var expandSubjectId = 0L private var expandSubjectId = 0L
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override suspend fun onViewReady(savedInstanceState: Bundle?) {
activity = (getActivity() as MainActivity?) ?: return null
context ?: return null
app = activity.application as App
b = AttendanceListFragmentBinding.inflate(inflater)
return b.root
}
override fun onPageCreated(): Boolean { startCoroutineTimer(100L) {
if (!isAdded) return@startCoroutineTimer
viewType = arguments?.getInt("viewType") ?: AttendanceFragment.VIEW_DAYS viewType = arguments?.getInt("viewType") ?: AttendanceFragment.VIEW_DAYS
expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L
@ -77,11 +55,9 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
b.list.apply { b.list.apply {
setHasFixedSize(true) setHasFixedSize(true)
layoutManager = LinearLayoutManager(context) layoutManager = LinearLayoutManager(context)
addOnScrollListener(onScrollListener)
} }
} }
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()
setSwipeToRefresh(adapter.items.isNullOrEmpty())
if (firstRun) { if (firstRun) {
expandSubject(adapter) expandSubject(adapter)
@ -90,7 +66,7 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
// show/hide relevant views // show/hide relevant views
b.progressBar.isVisible = false b.progressBar.isVisible = false
if (adapter.items.isNullOrEmpty()) { if (adapter.items.isEmpty()) {
b.list.isVisible = false b.list.isVisible = false
b.noData.isVisible = true b.noData.isVisible = true
} else { } else {
@ -102,7 +78,7 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
adapter.onAttendanceClick = { adapter.onAttendanceClick = {
AttendanceDetailsDialog(activity, it).show() AttendanceDetailsDialog(activity, it).show()
} }
}; return true} }
private fun expandSubject(adapter: AttendanceAdapter) { private fun expandSubject(adapter: AttendanceAdapter) {
var expandSubjectModel: GradesSubject? = null var expandSubjectModel: GradesSubject? = null

View File

@ -6,9 +6,6 @@ package pl.szczodrzynski.edziennik.ui.attendance
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AccelerateDecelerateInterpolator import android.view.animation.AccelerateDecelerateInterpolator
import android.view.animation.Animation import android.view.animation.Animation
import android.view.animation.Transformation import android.view.animation.Transformation
@ -19,8 +16,12 @@ import androidx.core.view.isInvisible
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.coroutines.* import kotlinx.coroutines.Dispatchers
import pl.szczodrzynski.edziennik.* import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Attendance import pl.szczodrzynski.edziennik.data.db.entity.Attendance
import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
import pl.szczodrzynski.edziennik.databinding.AttendanceSummaryFragmentBinding import pl.szczodrzynski.edziennik.databinding.AttendanceSummaryFragmentBinding
@ -30,43 +31,26 @@ import pl.szczodrzynski.edziennik.ext.setText
import pl.szczodrzynski.edziennik.ext.startCoroutineTimer import pl.szczodrzynski.edziennik.ext.startCoroutineTimer
import pl.szczodrzynski.edziennik.ui.attendance.AttendanceFragment.Companion.VIEW_SUMMARY import pl.szczodrzynski.edziennik.ui.attendance.AttendanceFragment.Companion.VIEW_SUMMARY
import pl.szczodrzynski.edziennik.ui.attendance.models.AttendanceSubject import pl.szczodrzynski.edziennik.ui.attendance.models.AttendanceSubject
import pl.szczodrzynski.edziennik.ui.base.lazypager.LazyFragment import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
import pl.szczodrzynski.edziennik.ui.grades.models.GradesSubject import pl.szczodrzynski.edziennik.ui.grades.models.GradesSubject
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import java.text.DecimalFormat import java.text.DecimalFormat
import kotlin.coroutines.CoroutineContext
class AttendanceSummaryFragment : LazyFragment(), CoroutineScope { class AttendanceSummaryFragment : BaseFragment<AttendanceSummaryFragmentBinding, MainActivity>(
inflater = AttendanceSummaryFragmentBinding::inflate,
) {
companion object { companion object {
private const val TAG = "AttendanceSummaryFragment"
private var periodSelection = 0 private var periodSelection = 0
} }
private lateinit var app: App override fun getRefreshScrollingView() = b.list
private lateinit var activity: MainActivity
private lateinit var b: AttendanceSummaryFragmentBinding
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local/private variables go here
private val manager private val manager
get() = app.attendanceManager get() = app.attendanceManager
private var expandSubjectId = 0L private var expandSubjectId = 0L
private var attendance = listOf<AttendanceFull>() private var attendance = listOf<AttendanceFull>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override suspend fun onViewReady(savedInstanceState: Bundle?) {
activity = (getActivity() as MainActivity?) ?: return null
context ?: return null
app = activity.application as App
b = AttendanceSummaryFragmentBinding.inflate(inflater)
return b.root
}
override fun onPageCreated(): Boolean { startCoroutineTimer(100L) {
if (!isAdded) return@startCoroutineTimer
expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L
val adapter = AttendanceAdapter(activity, VIEW_SUMMARY) val adapter = AttendanceAdapter(activity, VIEW_SUMMARY)
@ -91,7 +75,6 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
} }
} }
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()
setSwipeToRefresh(adapter.items.isNullOrEmpty())
if (firstRun) { if (firstRun) {
expandSubject(adapter) expandSubject(adapter)
@ -100,7 +83,7 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
// show/hide relevant views // show/hide relevant views
b.progressBar.isVisible = false b.progressBar.isVisible = false
if (adapter.items.isNullOrEmpty()) { if (adapter.items.isEmpty()) {
b.statsLayout.isVisible = false b.statsLayout.isVisible = false
b.list.isVisible = false b.list.isVisible = false
b.noData.isVisible = true b.noData.isVisible = true
@ -144,7 +127,7 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()
} }
} }
}; return true} }
private fun expandSubject(adapter: AttendanceAdapter) { private fun expandSubject(adapter: AttendanceAdapter) {
var expandSubjectModel: GradesSubject? = null var expandSubjectModel: GradesSubject? = null

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2021-10-18. * Copyright (c) Kuba Szczodrzyński 2021-10-18.
*/ */
package pl.szczodrzynski.edziennik.ui.dialogs.base package pl.szczodrzynski.edziennik.ui.base.dialog
import android.view.View import android.view.View
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2021-10-18. * Copyright (c) Kuba Szczodrzyński 2021-10-18.
*/ */
package pl.szczodrzynski.edziennik.ui.dialogs.base package pl.szczodrzynski.edziennik.ui.base.dialog
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2021-10-18. * Copyright (c) Kuba Szczodrzyński 2021-10-18.
*/ */
package pl.szczodrzynski.edziennik.ui.dialogs.base package pl.szczodrzynski.edziennik.ui.base.dialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2021-10-18. * Copyright (c) Kuba Szczodrzyński 2021-10-18.
*/ */
package pl.szczodrzynski.edziennik.ui.dialogs.base package pl.szczodrzynski.edziennik.ui.base.dialog
import android.view.View import android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity

View File

@ -0,0 +1,102 @@
/*
* Copyright (c) Kuba Szczodrzyński 2024-7-3.
*/
package pl.szczodrzynski.edziennik.ui.base.fragment
import android.annotation.SuppressLint
import android.view.MotionEvent
import android.view.View
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ui.login.LoginActivity
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
@SuppressLint("ClickableViewAccessibility")
internal fun BaseFragment<*, *>.setupCanRefresh() {
when (val view = getRefreshScrollingView()) {
is RecyclerView -> {
canRefresh = !view.canScrollVertically(-1)
view.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
// disable refresh when scrolled down
if (recyclerView.canScrollVertically(-1))
canRefresh = false
// enable refresh when scrolled to the top and not scrolling anymore
else if (newState == RecyclerView.SCROLL_STATE_IDLE)
canRefresh = true
}
})
}
is View -> {
canRefresh = !view.canScrollVertically(-1)
var isTouched = false
view.setOnTouchListener { _, event ->
// keep track of the touch state
when (event.action) {
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> isTouched = false
MotionEvent.ACTION_DOWN -> isTouched = true
}
// disable refresh when scrolled down
if (view.canScrollVertically(-1))
canRefresh = false
// enable refresh when scrolled to the top and not touching anymore
else if (!isTouched)
canRefresh = true
false
}
}
else -> {
// dispatch the default value to the activity
canRefresh = canRefresh
}
}
}
internal fun BaseFragment<*, *>.setupMainActivity(activity: MainActivity) {
val items = getBottomSheetItems().toMutableList()
getMarkAsReadType()?.let { metadataType ->
if (items.isNotEmpty())
items += BottomSheetSeparatorItem(true)
items += BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_mark_as_read)
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
.withOnClickListener {
activity.bottomSheet.close()
launch(Dispatchers.IO) {
app.db.metadataDao().setAllSeen(app.profileId, metadataType, true)
}
Toast.makeText(
activity,
R.string.main_menu_mark_as_read_success,
Toast.LENGTH_SHORT
).show()
}
}
if (items.isNotEmpty()) {
activity.navView.bottomSheet.prependItems(*items.toTypedArray())
}
getFab()?.let { (text, icon) ->
activity.navView.bottomBar.apply {
fabEnable = true
fabExtendedText = app.getString(text)
fabIcon = icon
setFabOnClickListener {
launch {
onFabClick()
}
}
}
}
}
internal fun BaseFragment<*, *>.setupLoginActivity(activity: LoginActivity) {}

View File

@ -0,0 +1,182 @@
/*
* Copyright (c) Kuba Szczodrzyński 2024-7-2.
*/
package pl.szczodrzynski.edziennik.ui.base.fragment
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding
import com.mikepenz.iconics.typeface.IIcon
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.data.enums.MetadataType
import pl.szczodrzynski.edziennik.ext.startCoroutineTimer
import pl.szczodrzynski.edziennik.ui.login.LoginActivity
import pl.szczodrzynski.navlib.bottomsheet.items.IBottomSheetItem
import kotlin.coroutines.CoroutineContext
@Suppress("UNCHECKED_CAST")
abstract class BaseFragment<B : ViewBinding, A : AppCompatActivity>(
private val inflater: ((inflater: LayoutInflater, parent: ViewGroup?, attachToParent: Boolean) -> B)?,
) : Fragment(), CoroutineScope {
internal lateinit var app: App
internal lateinit var activity: A
internal lateinit var b: B
private var isViewReady: Boolean = false
private var inState: Bundle? = null
/**
* Enables or disables the activity's SwipeRefreshLayout.
* Use only if [getRefreshScrollingView] is not used.
*
* The [PagerFragment] manages its [canRefresh] state
* based on the value of the currently selected page.
*/
internal var canRefresh = false
set(value) {
field = value
(activity as? MainActivity)?.swipeRefreshLayout?.isEnabled =
!canRefreshDisabled && value
}
/**
* Forcefully disables the activity's SwipeRefreshLayout
* if [getRefreshScrollingView] is used.
*/
internal var canRefreshDisabled = false
set(value) {
field = value
canRefresh = canRefresh
}
private var job = Job()
final override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
final override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
job.cancel()
job = Job()
activity = getActivity() as? A ?: return null
context ?: return null
app = activity.application as App
// inflate using the constructor parameter or the body method
b = this.inflater?.invoke(inflater, container, false)
?: inflate(inflater, container, false)
?: return null
isViewReady = false // reinitialize the view in onResume()
inState = savedInstanceState // save the instance state for onResume()
return b.root
}
override fun onResume() {
super.onResume()
try {
EventBus.getDefault().register(this)
} catch (_: Exception) {
}
if (!isAdded || isViewReady)
return
isViewReady = true
setupCanRefresh()
(activity as? MainActivity)?.let(::setupMainActivity)
(activity as? LoginActivity)?.let(::setupLoginActivity)
// let the UI transition for a moment
startCoroutineTimer(100L) {
if (!isAdded)
return@startCoroutineTimer
onViewReady(inState)
(activity as? MainActivity)?.gainAttention()
(activity as? MainActivity)?.gainAttentionFAB()
}
}
override fun onPause() {
super.onPause()
try {
EventBus.getDefault().unregister(this)
} catch (_: Exception) {
}
}
final override fun onDestroyView() {
super.onDestroyView()
isViewReady = false
job.cancel()
}
final override fun onViewCreated(view: View, savedInstanceState: Bundle?) =
super.onViewCreated(view, savedInstanceState)
final override fun onViewStateRestored(savedInstanceState: Bundle?) =
super.onViewStateRestored(savedInstanceState)
final override fun onAttach(context: Context) = super.onAttach(context)
final override fun onCreate(savedInstanceState: Bundle?) = super.onCreate(savedInstanceState)
final override fun onStart() = super.onStart()
final override fun onStop() = super.onStop()
final override fun onDestroy() = super.onDestroy()
final override fun onDetach() = super.onDetach()
/**
* Called if there is no inflater passed in the constructor.
* Must return a non-null value.
*/
open fun inflate(
inflater: LayoutInflater,
parent: ViewGroup?,
attachToParent: Boolean,
): B? = null
/**
* Called to retrieve the scrolling view contained in the fragment.
* The scrolling view is configured to act nicely with the SwipeRefreshLayout.
*/
open fun getRefreshScrollingView(): View? = null
/**
* Called to retrieve the FAB label resource and the icon.
* If provided, a FAB is added and shown automatically.
*/
open fun getFab(): Pair<Int, IIcon>? = null
/**
* Called to retrieve the [MetadataType] of items displayed by the fragment.
* If provided, a "mark as read" item is added to the bottom sheet.
*/
open fun getMarkAsReadType(): MetadataType? = null
/**
* Called to retrieve any extra bottom sheet items that should be displayed.
*/
open fun getBottomSheetItems() = listOf<IBottomSheetItem<*>>()
/**
* Called after the fragment is initialized (default) or when is becomes visible (pager).
*
* Perform view initialization and other tasks, if necessary. Remember to call super
* if used in a [PagerFragment].
*/
open suspend fun onViewReady(savedInstanceState: Bundle?) {}
/**
* Called when the FAB is clicked (if enabled).
*/
open suspend fun onFabClick() {}
}

View File

@ -0,0 +1,135 @@
/*
* Copyright (c) Kuba Szczodrzyński 2024-7-3.
*/
package pl.szczodrzynski.edziennik.ui.base.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.ext.set
abstract class PagerFragment<B : ViewBinding, A : AppCompatActivity>(
inflater: ((inflater: LayoutInflater, parent: ViewGroup?, attachToParent: Boolean) -> B)?,
) : BaseFragment<B, A>(inflater) {
private lateinit var pages: List<Pair<Fragment, String>>
private val fragmentCache = mutableMapOf<Int, Fragment>()
/**
* Stores the default page index that is activated when
* entering the fragment. Updated every time a new page
* is selected.
*
* Override with a getter and setter to make it backed by
* e.g. app.config. Set this value before calling super.[onViewReady]
* to provide a one-time default.
*/
protected open var savedPageSelection = -1
override suspend fun onViewReady(savedInstanceState: Bundle?) {
if (savedPageSelection == -1)
savedPageSelection = savedInstanceState?.getInt("pageSelection") ?: 0
pages = onCreatePages()
val adapter = object : FragmentStateAdapter(this) {
override fun getItemCount() = getPageCount()
override fun createFragment(position: Int): Fragment {
val fragment = getPageFragment(position)
fragmentCache[position] = fragment
return fragment
}
}
getViewPager().let {
it.offscreenPageLimit = 1
it.adapter = adapter
it.setCurrentItem(savedPageSelection, false)
it.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrollStateChanged(state: Int) {
canRefresh = when (state) {
ViewPager2.SCROLL_STATE_IDLE -> {
val fragment =
fragmentCache[it.currentItem] as? BaseFragment<*, *>
fragment != null && !fragment.canRefreshDisabled && fragment.canRefresh
}
else -> false
}
}
override fun onPageSelected(position: Int) {
savedPageSelection = position
launch {
this@PagerFragment.onPageSelected(position)
}
}
})
}
TabLayoutMediator(getTabLayout(), getViewPager()) { tab, position ->
tab.text = getPageTitle(position)
}.attach()
onPageSelected(savedPageSelection)
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState["pageSelection"] = getViewPager().currentItem
}
/**
* Navigates to the specified page with a smooth scroll animation.
*
* To navigate without smooth scroll, use [savedPageSelection] instead
* to provide a default page selection.
*/
protected fun goToPage(position: Int) {
getViewPager().setCurrentItem(position, true)
savedPageSelection = position
launch {
onPageSelected(position)
}
}
/**
* Called to retrieve the [TabLayout] view of the pager fragment.
*/
abstract fun getTabLayout(): TabLayout
/**
* Called to retrieve the [ViewPager2] view of the pager fragment.
*/
abstract fun getViewPager(): ViewPager2
/**
* Called to retrieve a list of fragments (and their titles) for the pager.
* Only used with the default implementation of [getPageCount], [getPageFragment]
* and [getPageTitle].
*/
open suspend fun onCreatePages() = listOf<Pair<Fragment, String>>()
open fun getPageCount() = pages.size
open fun getPageFragment(position: Int) = pages[position].first
open fun getPageTitle(position: Int) = pages[position].second
/**
* Called when a new page is selected, by either:
* - opening the fragment for the first time (default page),
* - calling [goToPage], or
* - navigating to another page by the user.
*
* The [savedPageSelection] is updated before this method is called.
*/
open suspend fun onPageSelected(position: Int) {}
}

View File

@ -1,18 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-30.
*/
package pl.szczodrzynski.edziennik.ui.base.lazypager
import androidx.fragment.app.FragmentManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
class FragmentLazyPagerAdapter(
fragmentManager: FragmentManager,
swipeRefreshLayout: SwipeRefreshLayout? = null,
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
}

View File

@ -1,56 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-29.
*/
package pl.szczodrzynski.edziennik.ui.base.lazypager
import android.os.Bundle
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
internal var onPageDestroy: ((position: Int, outState: Bundle?) -> 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()
}
}

View File

@ -1,28 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-29.
*/
package pl.szczodrzynski.edziennik.ui.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
}

View File

@ -1,42 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-29.
*/
package pl.szczodrzynski.edziennik.ui.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()
}
}
})
}
}

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2020-4-1. * Copyright (c) Kuba Szczodrzyński 2020-4-1.
*/ */
package pl.szczodrzynski.edziennik.ui.views package pl.szczodrzynski.edziennik.ui.base.views
import android.content.Context import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
@ -29,9 +29,9 @@ import pl.szczodrzynski.navlib.colorAttr
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class AttachmentAdapter( class AttachmentAdapter(
val context: Context, val context: Context,
val onAttachmentClick: (item: Item) -> Unit, val onAttachmentClick: (item: Item) -> Unit,
val onAttachmentLongClick: ((view: Chip, item: Item) -> Unit)? = null val onAttachmentLongClick: ((view: Chip, item: Item) -> Unit)? = null
) : RecyclerView.Adapter<AttachmentAdapter.ViewHolder>(), CoroutineScope { ) : RecyclerView.Adapter<AttachmentAdapter.ViewHolder>(), CoroutineScope {
companion object { companion object {
private const val TAG = "AttachmentAdapter" private const val TAG = "AttachmentAdapter"

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2020-4-1. * Copyright (c) Kuba Szczodrzyński 2020-4-1.
*/ */
package pl.szczodrzynski.edziennik.ui.views package pl.szczodrzynski.edziennik.ui.base.views
import android.content.Context import android.content.Context
import android.os.Build import android.os.Build
@ -19,7 +19,6 @@ import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent
import pl.szczodrzynski.edziennik.ext.get
import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
@ -46,7 +45,7 @@ class AttachmentsView @JvmOverloads constructor(
val activity = context as? AppCompatActivity ?: return val activity = context as? AppCompatActivity ?: return
val list = this as? RecyclerView ?: return val list = this as? RecyclerView ?: return
val profileId = arguments.get<Int>("profileId") ?: return val profileId = arguments.getInt("profileId")
val attachmentIds = arguments.getLongArray("attachmentIds") ?: return val attachmentIds = arguments.getLongArray("attachmentIds") ?: return
val attachmentNames = arguments.getStringArray("attachmentNames") ?: return val attachmentNames = arguments.getStringArray("attachmentNames") ?: return
val attachmentSizes = arguments.getLongArray("attachmentSizes") val attachmentSizes = arguments.getLongArray("attachmentSizes")

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2020-2-23. * Copyright (c) Kuba Szczodrzyński 2020-2-23.
*/ */
package pl.szczodrzynski.edziennik.ui.views package pl.szczodrzynski.edziennik.ui.base.views
import android.content.Context import android.content.Context
import android.content.ContextWrapper import android.content.ContextWrapper

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2021-4-14. * Copyright (c) Kuba Szczodrzyński 2021-4-14.
*/ */
package pl.szczodrzynski.edziennik.ui.views package pl.szczodrzynski.edziennik.ui.base.views
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2024-6-20. * Copyright (c) Kuba Szczodrzyński 2024-6-20.
*/ */
package pl.szczodrzynski.edziennik.ui.views package pl.szczodrzynski.edziennik.ui.base.views
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2020-2-23. * Copyright (c) Kuba Szczodrzyński 2020-2-23.
*/ */
package pl.szczodrzynski.edziennik.ui.views package pl.szczodrzynski.edziennik.ui.base.views
import android.content.Context import android.content.Context
import android.content.ContextWrapper import android.content.ContextWrapper

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2020-2-23. * Copyright (c) Kuba Szczodrzyński 2020-2-23.
*/ */
package pl.szczodrzynski.edziennik.ui.views package pl.szczodrzynski.edziennik.ui.base.views
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2020-3-7. * Copyright (c) Kuba Szczodrzyński 2020-3-7.
*/ */
package pl.szczodrzynski.edziennik.ui.views package pl.szczodrzynski.edziennik.ui.base.views
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2020-2-23. * Copyright (c) Kuba Szczodrzyński 2020-2-23.
*/ */
package pl.szczodrzynski.edziennik.ui.views package pl.szczodrzynski.edziennik.ui.base.views
import android.content.Context import android.content.Context
import android.content.ContextWrapper import android.content.ContextWrapper

View File

@ -1,63 +1,32 @@
package pl.szczodrzynski.edziennik.ui.behaviour package pl.szczodrzynski.edziennik.ui.behaviour
import android.graphics.Color import android.graphics.Color
import android.os.AsyncTask
import android.os.Bundle import android.os.Bundle
import android.view.* import android.view.View
import android.widget.Toast
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.App.Companion.profileId import pl.szczodrzynski.edziennik.App.Companion.profileId
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Notice import pl.szczodrzynski.edziennik.data.db.entity.Notice
import pl.szczodrzynski.edziennik.data.enums.MetadataType
import pl.szczodrzynski.edziennik.data.db.full.NoticeFull import pl.szczodrzynski.edziennik.data.db.full.NoticeFull
import pl.szczodrzynski.edziennik.data.enums.MetadataType
import pl.szczodrzynski.edziennik.databinding.FragmentBehaviourBinding import pl.szczodrzynski.edziennik.databinding.FragmentBehaviourBinding
import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
import pl.szczodrzynski.edziennik.utils.Themes.getPrimaryTextColor import pl.szczodrzynski.edziennik.utils.Themes.getPrimaryTextColor
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem import java.util.Locale
import java.util.*
class BehaviourFragment : Fragment() { class BehaviourFragment : BaseFragment<FragmentBehaviourBinding, MainActivity>(
inflater = FragmentBehaviourBinding::inflate,
) {
private lateinit var app: App override fun getMarkAsReadType() = MetadataType.NOTICE
private lateinit var activity: MainActivity
private lateinit var b: FragmentBehaviourBinding
private var displayMode = MODE_YEAR private var displayMode = MODE_YEAR
private var noticeList: List<NoticeFull>? = null private var noticeList: List<NoticeFull>? = null
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 = DataBindingUtil.inflate(inflater, R.layout.fragment_behaviour, container, false)
b.refreshLayout.setParent(activity.swipeRefreshLayout)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override suspend fun onViewReady(savedInstanceState: Bundle?) {
if (app == null || activity == null || b == null || !isAdded) return
activity.bottomSheet.prependItems(
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_mark_as_read)
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
.withOnClickListener { v3: View? ->
activity.bottomSheet.close()
AsyncTask.execute {
App.db.metadataDao().setAllSeen(profileId, MetadataType.NOTICE, true)
}
Toast.makeText(
activity,
R.string.main_menu_mark_as_read_success,
Toast.LENGTH_SHORT
).show()
}
)
b.toggleGroup.check(when (displayMode) { b.toggleGroup.check(when (displayMode) {
0 -> R.id.allYear 0 -> R.id.allYear
1 -> R.id.semester1 1 -> R.id.semester1
@ -81,18 +50,8 @@ class BehaviourFragment : Fragment() {
val linearLayoutManager = LinearLayoutManager(context) val linearLayoutManager = LinearLayoutManager(context)
b.noticesView.setHasFixedSize(true) b.noticesView.setHasFixedSize(true)
b.noticesView.layoutManager = linearLayoutManager b.noticesView.layoutManager = linearLayoutManager
b.noticesView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
if (recyclerView.canScrollVertically(-1)) {
b.refreshLayout.isEnabled = false
}
if (!recyclerView.canScrollVertically(-1) && newState == RecyclerView.SCROLL_STATE_IDLE) {
b.refreshLayout.isEnabled = true
}
}
})
App.db.noticeDao().getAll(profileId).observe(viewLifecycleOwner) { notices: List<NoticeFull>? -> App.db.noticeDao().getAll(profileId).observe(viewLifecycleOwner) { notices: List<NoticeFull>? ->
if (app == null || activity == null || b == null || !isAdded) return@observe if (!isAdded) return@observe
if (notices == null) { if (notices == null) {
b.noticesView.visibility = View.GONE b.noticesView.visibility = View.GONE
b.noticesNoData.visibility = View.VISIBLE b.noticesNoData.visibility = View.VISIBLE

View File

@ -13,7 +13,7 @@ import kotlinx.coroutines.*
import okhttp3.* import okhttp3.*
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.RecaptchaDialogBinding import pl.szczodrzynski.edziennik.databinding.RecaptchaDialogBinding
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BindingDialog
import java.io.IOException import java.io.IOException
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine

View File

@ -11,7 +11,7 @@ import androidx.appcompat.app.AppCompatActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.RecaptchaViewBinding import pl.szczodrzynski.edziennik.databinding.RecaptchaViewBinding
import pl.szczodrzynski.edziennik.ext.onClick import pl.szczodrzynski.edziennik.ext.onClick
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BindingDialog
class RecaptchaPromptDialog( class RecaptchaPromptDialog(
activity: AppCompatActivity, activity: AppCompatActivity,

View File

@ -4,73 +4,20 @@
package pl.szczodrzynski.edziennik.ui.debug package pl.szczodrzynski.edziennik.ui.debug
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import pl.szczodrzynski.edziennik.databinding.BasePagerFragmentBinding
import kotlinx.coroutines.Job import pl.szczodrzynski.edziennik.ui.base.fragment.PagerFragment
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.databinding.TemplateFragmentBinding
import pl.szczodrzynski.edziennik.ext.addOnPageSelectedListener
import pl.szczodrzynski.edziennik.ui.base.lazypager.FragmentLazyPagerAdapter
import pl.szczodrzynski.edziennik.ui.login.LoginActivity
import pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoTouch
import kotlin.coroutines.CoroutineContext
class LabFragment : Fragment(), CoroutineScope { class LabFragment : PagerFragment<BasePagerFragmentBinding, AppCompatActivity>(
companion object { inflater = BasePagerFragmentBinding::inflate,
private const val TAG = "LabFragment" ), CoroutineScope {
var pageSelection = 0
}
private lateinit var app: App override fun getTabLayout() = b.tabLayout
private lateinit var activity: AppCompatActivity override fun getViewPager() = b.viewPager
private lateinit var b: TemplateFragmentBinding override suspend fun onCreatePages() = listOf(
LabPageFragment() to "click me",
private val job: Job = Job() LabProfileFragment() to "JSON",
override val coroutineContext: CoroutineContext LabPlaygroundFragment() to "Playground",
get() = job + Dispatchers.Main )
// local/private variables go here
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as AppCompatActivity?) ?: return null
context ?: return null
app = activity.application as App
b = TemplateFragmentBinding.inflate(inflater)
when (activity) {
is MainActivity -> b.refreshLayout.setParent((activity as MainActivity).swipeRefreshLayout)
is LoginActivity -> b.refreshLayout.setParent((activity as LoginActivity).swipeRefreshLayout)
}
b.refreshLayout.isEnabled = false
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (!isAdded) return
val pagerAdapter = FragmentLazyPagerAdapter(
parentFragmentManager,
b.refreshLayout,
listOf(
LabPageFragment() to "click me",
LabProfileFragment() to "JSON",
LabPlaygroundFragment() to "Playground",
)
)
b.viewPager.apply {
offscreenPageLimit = 1
adapter = pagerAdapter
currentItem = pageSelection
addOnPageSelectedListener {
pageSelection = it
}
b.tabLayout.setupWithViewPager(this)
}
}
} }

View File

@ -6,9 +6,6 @@ package pl.szczodrzynski.edziennik.ui.debug
import android.os.Bundle import android.os.Bundle
import android.os.Process import android.os.Process
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.widget.doAfterTextChanged import androidx.core.widget.doAfterTextChanged
@ -16,47 +13,38 @@ import androidx.sqlite.db.SimpleSQLiteQuery
import com.chuckerteam.chucker.api.Chucker import com.chuckerteam.chucker.api.Chucker
import com.chuckerteam.chucker.api.Chucker.SCREEN_HTTP import com.chuckerteam.chucker.api.Chucker.SCREEN_HTTP
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.config.Config import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.SignatureInterceptor import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.SignatureInterceptor
import pl.szczodrzynski.edziennik.data.config.Config
import pl.szczodrzynski.edziennik.data.db.entity.EventType.Companion.SOURCE_DEFAULT import pl.szczodrzynski.edziennik.data.db.entity.EventType.Companion.SOURCE_DEFAULT
import pl.szczodrzynski.edziennik.databinding.LabFragmentBinding import pl.szczodrzynski.edziennik.databinding.LabFragmentBinding
import pl.szczodrzynski.edziennik.ext.* import pl.szczodrzynski.edziennik.ext.asBoldSpannable
import pl.szczodrzynski.edziennik.ui.base.lazypager.LazyFragment import pl.szczodrzynski.edziennik.ext.asColoredSpannable
import pl.szczodrzynski.edziennik.ext.asItalicSpannable
import pl.szczodrzynski.edziennik.ext.asUnderlineSpannable
import pl.szczodrzynski.edziennik.ext.concat
import pl.szczodrzynski.edziennik.ext.onChange
import pl.szczodrzynski.edziennik.ext.onClick
import pl.szczodrzynski.edziennik.ext.resolveAttr
import pl.szczodrzynski.edziennik.ext.startCoroutineTimer
import pl.szczodrzynski.edziennik.ext.takeValue
import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
import pl.szczodrzynski.edziennik.ui.dialogs.ProfileRemoveDialog import pl.szczodrzynski.edziennik.ui.dialogs.ProfileRemoveDialog
import pl.szczodrzynski.edziennik.utils.TextInputDropDown import pl.szczodrzynski.edziennik.utils.TextInputDropDown
import pl.szczodrzynski.fslogin.decode import pl.szczodrzynski.fslogin.decode
import kotlin.coroutines.CoroutineContext
import kotlin.system.exitProcess import kotlin.system.exitProcess
class LabPageFragment : LazyFragment(), CoroutineScope { class LabPageFragment : BaseFragment<LabFragmentBinding, AppCompatActivity>(
companion object { inflater = LabFragmentBinding::inflate,
private const val TAG = "LabPageFragment" ) {
}
private lateinit var app: App override fun getRefreshScrollingView() = b.scrollView
private lateinit var activity: AppCompatActivity
private lateinit var b: LabFragmentBinding
private val job: Job = Job() override suspend fun onViewReady(savedInstanceState: Bundle?) {
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 AppCompatActivity?) ?: return null
context ?: return null
app = activity.application as App
b = LabFragmentBinding.inflate(inflater)
return b.root
}
override fun onPageCreated(): Boolean {
b.app = app b.app = app
if (app.profile.id == 0) { if (app.profile.id == 0) {
@ -222,7 +210,5 @@ class LabPageFragment : LazyFragment(), CoroutineScope {
}.concat("\n\n") }.concat("\n\n")
b.cookies.text = text b.cookies.text = text
} }
return true
} }
} }

View File

@ -5,51 +5,21 @@
package pl.szczodrzynski.edziennik.ui.debug package pl.szczodrzynski.edziennik.ui.debug
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.databinding.LabPlaygroundBinding import pl.szczodrzynski.edziennik.databinding.LabPlaygroundBinding
import pl.szczodrzynski.edziennik.ext.asColoredSpannable import pl.szczodrzynski.edziennik.ext.asColoredSpannable
import pl.szczodrzynski.edziennik.ext.concat import pl.szczodrzynski.edziennik.ext.concat
import pl.szczodrzynski.edziennik.ext.resolveAttr import pl.szczodrzynski.edziennik.ext.resolveAttr
import pl.szczodrzynski.edziennik.ui.base.lazypager.LazyFragment import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
import timber.log.Timber import timber.log.Timber
import kotlin.coroutines.CoroutineContext
class LabPlaygroundFragment : LazyFragment(), CoroutineScope { class LabPlaygroundFragment : BaseFragment<LabPlaygroundBinding, AppCompatActivity>(
companion object { inflater = LabPlaygroundBinding::inflate,
private const val TAG = "LabPlaygroundFragment" ) {
}
private lateinit var app: App override fun getRefreshScrollingView() = b.root
private lateinit var activity: AppCompatActivity
private lateinit var b: LabPlaygroundBinding
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 AppCompatActivity?) ?: return null
context ?: return null
app = activity.application as App
b = LabPlaygroundBinding.inflate(inflater)
return b.root
}
override fun onPageCreated(): Boolean {
override suspend fun onViewReady(savedInstanceState: Bundle?) {
Timber.d("textColorSecondary: ${android.R.attr.textColorSecondary.resolveAttr(activity)}") Timber.d("textColorSecondary: ${android.R.attr.textColorSecondary.resolveAttr(activity)}")
b.spanTest1.text = listOf( b.spanTest1.text = listOf(
"Text:", "android:textColorSecondary spannable (activity)".asColoredSpannable( "Text:", "android:textColorSecondary spannable (activity)".asColoredSpannable(
@ -62,7 +32,5 @@ class LabPlaygroundFragment : LazyFragment(), CoroutineScope {
android.R.attr.textColorSecondary.resolveAttr(context) android.R.attr.textColorSecondary.resolveAttr(context)
) )
).concat(" ") ).concat(" ")
return true
} }
} }

View File

@ -5,9 +5,6 @@
package pl.szczodrzynski.edziennik.ui.debug package pl.szczodrzynski.edziennik.ui.debug
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@ -16,48 +13,30 @@ import com.google.gson.JsonArray
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonParser import com.google.gson.JsonParser
import com.google.gson.JsonPrimitive import com.google.gson.JsonPrimitive
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.MainActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.databinding.TemplateListPageFragmentBinding import pl.szczodrzynski.edziennik.databinding.TemplateListPageFragmentBinding
import pl.szczodrzynski.edziennik.ext.* import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.ui.base.lazypager.LazyFragment import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
import pl.szczodrzynski.edziennik.ui.login.LoginActivity import pl.szczodrzynski.edziennik.ui.login.LoginActivity
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import kotlin.coroutines.CoroutineContext
class LabProfileFragment : LazyFragment(), CoroutineScope { class LabProfileFragment : BaseFragment<TemplateListPageFragmentBinding, AppCompatActivity>(
inflater = TemplateListPageFragmentBinding::inflate,
) {
companion object { companion object {
private const val TAG = "LabProfileFragment" private const val TAG = "LabProfileFragment"
} }
private lateinit var app: App override fun getRefreshScrollingView() = b.list
private lateinit var activity: AppCompatActivity
private lateinit var b: TemplateListPageFragmentBinding
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local/private variables go here
private lateinit var adapter: LabJsonAdapter private lateinit var adapter: LabJsonAdapter
private val loginStore by lazy { private val loginStore by lazy {
app.db.loginStoreDao().getByIdNow(app.profile.loginStoreId) app.db.loginStoreDao().getByIdNow(app.profile.loginStoreId)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override suspend fun onViewReady(savedInstanceState: Bundle?) {
activity = (getActivity() as AppCompatActivity?) ?: return null
context ?: return null
app = activity.application as App
b = TemplateListPageFragmentBinding.inflate(inflater)
return b.root
}
override fun onPageCreated(): Boolean { startCoroutineTimer(100L) {
adapter = LabJsonAdapter(activity, onJsonElementClick = { item -> adapter = LabJsonAdapter(activity, onJsonElementClick = { item ->
try { try {
var parent: Any = Unit var parent: Any = Unit
@ -117,7 +96,12 @@ class LabProfileFragment : LazyFragment(), CoroutineScope {
is JsonArray -> { is JsonArray -> {
} }
is HashMap<*, *> -> app.config[objName] = input is HashMap<*, *> -> {
if ("(profile)" in item.key)
app.profile.config[objName] = input
else
app.config[objName] = input
}
else -> { else -> {
val field = parent::class.java.getDeclaredField(objName) val field = parent::class.java.getDeclaredField(objName)
field.isAccessible = true field.isAccessible = true
@ -165,15 +149,13 @@ class LabProfileFragment : LazyFragment(), CoroutineScope {
setHasFixedSize(true) setHasFixedSize(true)
layoutManager = LinearLayoutManager(context) layoutManager = LinearLayoutManager(context)
addItemDecoration(SimpleDividerItemDecoration(context)) addItemDecoration(SimpleDividerItemDecoration(context))
addOnScrollListener(onScrollListener)
} }
// show/hide relevant views // show/hide relevant views
b.progressBar.isVisible = false b.progressBar.isVisible = false
b.list.isVisible = true b.list.isVisible = true
b.noData.isVisible = false b.noData.isVisible = false
}
}; return true }
private fun showJson() { private fun showJson() {
val json = JsonObject().also { json -> val json = JsonObject().also { json ->

View File

@ -13,7 +13,7 @@ import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.DialogBellSyncBinding import pl.szczodrzynski.edziennik.databinding.DialogBellSyncBinding
import pl.szczodrzynski.edziennik.ext.resolveDrawable import pl.szczodrzynski.edziennik.ext.resolveDrawable
import pl.szczodrzynski.edziennik.ext.startCoroutineTimer import pl.szczodrzynski.edziennik.ext.startCoroutineTimer
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BindingDialog
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
class BellSyncDialog( class BellSyncDialog(

View File

@ -13,7 +13,7 @@ import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Lesson import pl.szczodrzynski.edziennik.data.db.entity.Lesson
import pl.szczodrzynski.edziennik.databinding.DialogBellSyncTimeChooseBinding import pl.szczodrzynski.edziennik.databinding.DialogBellSyncTimeChooseBinding
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BindingDialog
import pl.szczodrzynski.edziennik.utils.TextInputDropDown import pl.szczodrzynski.edziennik.utils.TextInputDropDown
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time

View File

@ -9,7 +9,7 @@ import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ext.dp import pl.szczodrzynski.edziennik.ext.dp
import pl.szczodrzynski.edziennik.ui.dialogs.base.ViewDialog import pl.szczodrzynski.edziennik.ui.base.dialog.ViewDialog
import pl.szczodrzynski.edziennik.utils.BetterLinkMovementMethod import pl.szczodrzynski.edziennik.utils.BetterLinkMovementMethod
import pl.szczodrzynski.edziennik.utils.html.BetterHtml import pl.szczodrzynski.edziennik.utils.html.BetterHtml

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2020-2-16. * Copyright (c) Kuba Szczodrzyński 2020-2-16.
*/ */
package pl.szczodrzynski.edziennik.ui.error package pl.szczodrzynski.edziennik.ui.dialogs
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@ -13,7 +13,7 @@ import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.ext.* import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BaseDialog
class ErrorDetailsDialog( class ErrorDetailsDialog(
activity: AppCompatActivity, activity: AppCompatActivity,

View File

@ -10,7 +10,7 @@ import kotlinx.coroutines.*
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BaseDialog
class ProfileRemoveDialog( class ProfileRemoveDialog(
activity: AppCompatActivity, activity: AppCompatActivity,

View File

@ -8,7 +8,7 @@ import androidx.appcompat.app.AppCompatActivity
import me.dm7.barcodescanner.zxing.ZXingScannerView import me.dm7.barcodescanner.zxing.ZXingScannerView
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ext.dp import pl.szczodrzynski.edziennik.ext.dp
import pl.szczodrzynski.edziennik.ui.dialogs.base.ViewDialog import pl.szczodrzynski.edziennik.ui.base.dialog.ViewDialog
class QrScannerDialog( class QrScannerDialog(
activity: AppCompatActivity, activity: AppCompatActivity,

View File

@ -13,7 +13,7 @@ import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.StyledTextDialogBinding import pl.szczodrzynski.edziennik.databinding.StyledTextDialogBinding
import pl.szczodrzynski.edziennik.ext.isNightMode import pl.szczodrzynski.edziennik.ext.isNightMode
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BindingDialog
import pl.szczodrzynski.edziennik.utils.DefaultTextStyles import pl.szczodrzynski.edziennik.utils.DefaultTextStyles
import pl.szczodrzynski.edziennik.core.manager.TextStylingManager.HtmlMode.SIMPLE import pl.szczodrzynski.edziennik.core.manager.TextStylingManager.HtmlMode.SIMPLE
import pl.szczodrzynski.edziennik.core.manager.TextStylingManager.StylingConfig import pl.szczodrzynski.edziennik.core.manager.TextStylingManager.StylingConfig

View File

@ -10,7 +10,7 @@ import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.databinding.DialogConfigAgendaBinding import pl.szczodrzynski.edziennik.databinding.DialogConfigAgendaBinding
import pl.szczodrzynski.edziennik.ext.onChange import pl.szczodrzynski.edziennik.ext.onChange
import pl.szczodrzynski.edziennik.ui.dialogs.base.ConfigDialog import pl.szczodrzynski.edziennik.ui.base.dialog.ConfigDialog
class AgendaConfigDialog( class AgendaConfigDialog(
activity: AppCompatActivity, activity: AppCompatActivity,

View File

@ -6,7 +6,7 @@ package pl.szczodrzynski.edziennik.ui.dialogs.settings
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BaseDialog
class AppLanguageDialog( class AppLanguageDialog(
activity: AppCompatActivity, activity: AppCompatActivity,

View File

@ -9,7 +9,7 @@ import androidx.appcompat.app.AppCompatActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.AttendanceConfigDialogBinding import pl.szczodrzynski.edziennik.databinding.AttendanceConfigDialogBinding
import pl.szczodrzynski.edziennik.ext.onChange import pl.szczodrzynski.edziennik.ext.onChange
import pl.szczodrzynski.edziennik.ui.dialogs.base.ConfigDialog import pl.szczodrzynski.edziennik.ui.base.dialog.ConfigDialog
class AttendanceConfigDialog( class AttendanceConfigDialog(
activity: AppCompatActivity, activity: AppCompatActivity,

View File

@ -10,7 +10,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.DialogEditTextBinding import pl.szczodrzynski.edziennik.databinding.DialogEditTextBinding
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BindingDialog
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
class BellSyncConfigDialog( class BellSyncConfigDialog(

View File

@ -16,7 +16,7 @@ import pl.szczodrzynski.edziennik.ext.join
import pl.szczodrzynski.edziennik.ext.onChange import pl.szczodrzynski.edziennik.ext.onChange
import pl.szczodrzynski.edziennik.ext.onClick import pl.szczodrzynski.edziennik.ext.onClick
import pl.szczodrzynski.edziennik.ext.setOnSelectedListener import pl.szczodrzynski.edziennik.ext.setOnSelectedListener
import pl.szczodrzynski.edziennik.ui.dialogs.base.ConfigDialog import pl.szczodrzynski.edziennik.ui.base.dialog.ConfigDialog
import pl.szczodrzynski.edziennik.core.manager.GradesManager.Companion.COLOR_MODE_DEFAULT import pl.szczodrzynski.edziennik.core.manager.GradesManager.Companion.COLOR_MODE_DEFAULT
import pl.szczodrzynski.edziennik.core.manager.GradesManager.Companion.COLOR_MODE_WEIGHTED import pl.szczodrzynski.edziennik.core.manager.GradesManager.Companion.COLOR_MODE_WEIGHTED
import pl.szczodrzynski.edziennik.core.manager.GradesManager.Companion.ORDER_BY_DATE_DESC import pl.szczodrzynski.edziennik.core.manager.GradesManager.Companion.ORDER_BY_DATE_DESC

View File

@ -8,7 +8,7 @@ import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.MessagesConfigDialogBinding import pl.szczodrzynski.edziennik.databinding.MessagesConfigDialogBinding
import pl.szczodrzynski.edziennik.ui.dialogs.base.ConfigDialog import pl.szczodrzynski.edziennik.ui.base.dialog.ConfigDialog
class MessagesConfigDialog( class MessagesConfigDialog(
activity: AppCompatActivity, activity: AppCompatActivity,

View File

@ -11,7 +11,7 @@ import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ext.resolveString import pl.szczodrzynski.edziennik.ext.resolveString
import pl.szczodrzynski.edziennik.data.enums.NavTarget import pl.szczodrzynski.edziennik.data.enums.NavTarget
import pl.szczodrzynski.edziennik.data.enums.NavTargetLocation import pl.szczodrzynski.edziennik.data.enums.NavTargetLocation
import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BaseDialog
class MiniMenuConfigDialog( class MiniMenuConfigDialog(
activity: AppCompatActivity, activity: AppCompatActivity,

View File

@ -9,7 +9,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.enums.NotificationType import pl.szczodrzynski.edziennik.data.enums.NotificationType
import pl.szczodrzynski.edziennik.ext.resolveString import pl.szczodrzynski.edziennik.ext.resolveString
import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BaseDialog
class NotificationFilterDialog( class NotificationFilterDialog(
activity: AppCompatActivity, activity: AppCompatActivity,

View File

@ -16,7 +16,7 @@ import pl.szczodrzynski.edziennik.ext.dp
import pl.szczodrzynski.edziennik.ext.onChange import pl.szczodrzynski.edziennik.ext.onChange
import pl.szczodrzynski.edziennik.ext.onClick import pl.szczodrzynski.edziennik.ext.onClick
import pl.szczodrzynski.edziennik.ui.dialogs.ProfileRemoveDialog import pl.szczodrzynski.edziennik.ui.dialogs.ProfileRemoveDialog
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BindingDialog
class ProfileConfigDialog( class ProfileConfigDialog(
activity: MainActivity, activity: MainActivity,

View File

@ -9,7 +9,7 @@ import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ext.HOUR import pl.szczodrzynski.edziennik.ext.HOUR
import pl.szczodrzynski.edziennik.ext.MINUTE import pl.szczodrzynski.edziennik.ext.MINUTE
import pl.szczodrzynski.edziennik.ext.getSyncInterval import pl.szczodrzynski.edziennik.ext.getSyncInterval
import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BaseDialog
class SyncIntervalDialog( class SyncIntervalDialog(
activity: AppCompatActivity, activity: AppCompatActivity,

View File

@ -7,7 +7,7 @@ package pl.szczodrzynski.edziennik.ui.dialogs.settings
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.enums.Theme import pl.szczodrzynski.edziennik.data.enums.Theme
import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BaseDialog
class ThemeChooserDialog( class ThemeChooserDialog(
activity: AppCompatActivity, activity: AppCompatActivity,

View File

@ -9,7 +9,7 @@ import androidx.appcompat.app.AppCompatActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.TimetableConfigDialogBinding import pl.szczodrzynski.edziennik.databinding.TimetableConfigDialogBinding
import pl.szczodrzynski.edziennik.ext.Intent import pl.szczodrzynski.edziennik.ext.Intent
import pl.szczodrzynski.edziennik.ui.dialogs.base.ConfigDialog import pl.szczodrzynski.edziennik.ui.base.dialog.ConfigDialog
import pl.szczodrzynski.edziennik.ui.timetable.TimetableFragment import pl.szczodrzynski.edziennik.ui.timetable.TimetableFragment
class TimetableConfigDialog( class TimetableConfigDialog(

View File

@ -13,7 +13,7 @@ import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.szkolny.response.RegisterAvailabilityStatus import pl.szczodrzynski.edziennik.data.api.szkolny.response.RegisterAvailabilityStatus
import pl.szczodrzynski.edziennik.databinding.DialogRegisterUnavailableBinding import pl.szczodrzynski.edziennik.databinding.DialogRegisterUnavailableBinding
import pl.szczodrzynski.edziennik.ext.onClick import pl.szczodrzynski.edziennik.ext.onClick
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BindingDialog
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
class RegisterUnavailableDialog( class RegisterUnavailableDialog(

View File

@ -6,7 +6,7 @@ package pl.szczodrzynski.edziennik.ui.dialogs.sync
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BaseDialog
class ServerMessageDialog( class ServerMessageDialog(
activity: AppCompatActivity, activity: AppCompatActivity,

View File

@ -13,7 +13,7 @@ import pl.szczodrzynski.edziennik.data.enums.FeatureType
import pl.szczodrzynski.edziennik.ext.hasFeature import pl.szczodrzynski.edziennik.ext.hasFeature
import pl.szczodrzynski.edziennik.ext.resolveString import pl.szczodrzynski.edziennik.ext.resolveString
import pl.szczodrzynski.edziennik.data.enums.NavTarget import pl.szczodrzynski.edziennik.data.enums.NavTarget
import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BaseDialog
import pl.szczodrzynski.edziennik.ui.messages.list.MessagesFragment import pl.szczodrzynski.edziennik.ui.messages.list.MessagesFragment
class SyncViewListDialog( class SyncViewListDialog(

View File

@ -10,7 +10,7 @@ import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
import pl.szczodrzynski.edziennik.ext.Intent import pl.szczodrzynski.edziennik.ext.Intent
import pl.szczodrzynski.edziennik.sync.UpdateDownloaderService import pl.szczodrzynski.edziennik.sync.UpdateDownloaderService
import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BaseDialog
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.html.BetterHtml import pl.szczodrzynski.edziennik.utils.html.BetterHtml

View File

@ -19,7 +19,7 @@ import pl.szczodrzynski.edziennik.databinding.UpdateProgressDialogBinding
import pl.szczodrzynski.edziennik.ext.getInt import pl.szczodrzynski.edziennik.ext.getInt
import pl.szczodrzynski.edziennik.ext.startCoroutineTimer import pl.szczodrzynski.edziennik.ext.startCoroutineTimer
import pl.szczodrzynski.edziennik.core.work.UpdateStateEvent import pl.szczodrzynski.edziennik.core.work.UpdateStateEvent
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BindingDialog
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
class UpdateProgressDialog( class UpdateProgressDialog(

View File

@ -1,46 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-13.
*/
package pl.szczodrzynski.edziennik.ui.error
import android.util.Log
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.DialogLessonDetailsBinding
import kotlin.coroutines.CoroutineContext
class ErrorDialog(
val activity: AppCompatActivity,
val exception: Exception
) : CoroutineScope {
companion object {
private const val TAG = "ErrorDialog"
}
private lateinit var job: Job
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private val app by lazy { activity.application as App }
private lateinit var b: DialogLessonDetailsBinding
private lateinit var dialog: AlertDialog
init { run {
job = Job()
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.error_occured)
.setMessage(exception.message + "\n" + Log.getStackTraceString(exception))
.setPositiveButton(R.string.ok) { _, _ ->
dialog.dismiss()
}
.show()
}}
}

View File

@ -28,7 +28,7 @@ import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.databinding.DialogEventDetailsBinding import pl.szczodrzynski.edziennik.databinding.DialogEventDetailsBinding
import pl.szczodrzynski.edziennik.ext.* import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.data.enums.NavTarget import pl.szczodrzynski.edziennik.data.enums.NavTarget
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BindingDialog
import pl.szczodrzynski.edziennik.ui.notes.setupNotesButton import pl.szczodrzynski.edziennik.ui.notes.setupNotesButton
import pl.szczodrzynski.edziennik.ui.timetable.TimetableFragment import pl.szczodrzynski.edziennik.ui.timetable.TimetableFragment
import pl.szczodrzynski.edziennik.utils.BetterLink import pl.szczodrzynski.edziennik.utils.BetterLink

View File

@ -44,9 +44,9 @@ import pl.szczodrzynski.edziennik.ext.onClick
import pl.szczodrzynski.edziennik.ext.removeFromParent import pl.szczodrzynski.edziennik.ext.removeFromParent
import pl.szczodrzynski.edziennik.ext.setText import pl.szczodrzynski.edziennik.ext.setText
import pl.szczodrzynski.edziennik.ext.setTintColor import pl.szczodrzynski.edziennik.ext.setTintColor
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BindingDialog
import pl.szczodrzynski.edziennik.ui.dialogs.settings.RegistrationConfigDialog import pl.szczodrzynski.edziennik.ui.dialogs.settings.RegistrationConfigDialog
import pl.szczodrzynski.edziennik.ui.views.TimeDropdown.Companion.DISPLAY_LESSONS import pl.szczodrzynski.edziennik.ui.base.views.TimeDropdown.Companion.DISPLAY_LESSONS
import pl.szczodrzynski.edziennik.utils.Anim import pl.szczodrzynski.edziennik.utils.Anim
import pl.szczodrzynski.edziennik.utils.html.BetterHtml import pl.szczodrzynski.edziennik.utils.html.BetterHtml
import pl.szczodrzynski.edziennik.core.manager.TextStylingManager.HtmlMode.SIMPLE import pl.szczodrzynski.edziennik.core.manager.TextStylingManager.HtmlMode.SIMPLE

View File

@ -6,20 +6,18 @@ import android.graphics.BitmapFactory
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import android.widget.PopupMenu import android.widget.PopupMenu
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import coil.imageLoader import coil.imageLoader
import coil.request.ImageRequest import coil.request.ImageRequest
import com.github.bassaer.chatmessageview.model.IChatUser import com.github.bassaer.chatmessageview.model.IChatUser
import com.github.bassaer.chatmessageview.model.Message import com.github.bassaer.chatmessageview.model.Message
import com.github.bassaer.chatmessageview.view.ChatView import com.github.bassaer.chatmessageview.view.ChatView
import kotlinx.coroutines.* import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
@ -31,25 +29,16 @@ import pl.szczodrzynski.edziennik.data.db.entity.FeedbackMessage
import pl.szczodrzynski.edziennik.databinding.FragmentFeedbackBinding import pl.szczodrzynski.edziennik.databinding.FragmentFeedbackBinding
import pl.szczodrzynski.edziennik.ext.crc16 import pl.szczodrzynski.edziennik.ext.crc16
import pl.szczodrzynski.edziennik.ext.onClick import pl.szczodrzynski.edziennik.ext.onClick
import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.Utils.openUrl import pl.szczodrzynski.edziennik.utils.Utils.openUrl
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.Calendar
import kotlin.collections.set import kotlin.collections.set
import kotlin.coroutines.CoroutineContext
class FeedbackFragment : Fragment(), CoroutineScope { class FeedbackFragment : BaseFragment<FragmentFeedbackBinding, AppCompatActivity>(
companion object { inflater = FragmentFeedbackBinding::inflate,
private const val TAG = "FeedbackFragment" ) {
}
private lateinit var app: App
private lateinit var activity: AppCompatActivity
private lateinit var b: FragmentFeedbackBinding
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private val chatView: ChatView by lazy { b.chatView } private val chatView: ChatView by lazy { b.chatView }
private val api by lazy { SzkolnyApi(app) } private val api by lazy { SzkolnyApi(app) }
@ -58,18 +47,6 @@ class FeedbackFragment : Fragment(), CoroutineScope {
private var receiver: BroadcastReceiver? = null private var receiver: BroadcastReceiver? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as AppCompatActivity?) ?: return null
if (context == null)
return null
app = activity.application as App
// activity, context and profile is valid
b = FragmentFeedbackBinding.inflate(inflater)
// prevent doubled received messages on enter
EventBus.getDefault().removeStickyEvent(FeedbackMessageEvent::class.java)
return b.root
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true) @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onFeedbackMessageEvent(event: FeedbackMessageEvent) { fun onFeedbackMessageEvent(event: FeedbackMessageEvent) {
EventBus.getDefault().removeStickyEvent(event) EventBus.getDefault().removeStickyEvent(event)
@ -143,10 +120,8 @@ class FeedbackFragment : Fragment(), CoroutineScope {
} }
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override suspend fun onViewReady(savedInstanceState: Bundle?) {
// TODO check if app, activity, b can be null EventBus.getDefault().removeStickyEvent(FeedbackMessageEvent::class.java)
if (!isAdded)
return
b.faqText.setOnClickListener { openFaq() } b.faqText.setOnClickListener { openFaq() }
b.faqButton.setOnClickListener { openFaq() } b.faqButton.setOnClickListener { openFaq() }
@ -163,46 +138,44 @@ class FeedbackFragment : Fragment(), CoroutineScope {
setMessageMarginBottom(5) setMessageMarginBottom(5)
} }
launch { val messages = withContext(Dispatchers.IO) {
val messages = withContext(Dispatchers.Default) { val messages = app.db.feedbackMessageDao().allNow
val messages = app.db.feedbackMessageDao().allNow isDev = App.devMode && messages.any { it.deviceId != null }
isDev = App.devMode && messages.any { it.deviceId != null } messages
messages }
}
b.targetDeviceLayout.visibility = if (isDev) View.VISIBLE else View.GONE b.targetDeviceLayout.visibility = if (isDev) View.VISIBLE else View.GONE
b.targetDeviceDropDown.onClick { b.targetDeviceDropDown.onClick {
launchDeviceSelection() launchDeviceSelection()
} }
if (isDev) { if (isDev) {
messages.firstOrNull { it.received && it.devId == null }?.let { messages.firstOrNull { it.received && it.devId == null }?.let {
currentDeviceId = it.deviceId currentDeviceId = it.deviceId
b.targetDeviceDropDown.setText("${it.senderName} (${it.deviceId}) - ${it.deviceName}")
}
// handle notification intent
arguments?.getString("feedbackMessageDeviceId")?.let { deviceId ->
messages.firstOrNull { it.received && it.deviceId == deviceId && it.devId == null }?.let {
currentDeviceId = deviceId
b.targetDeviceDropDown.setText("${it.senderName} (${it.deviceId}) - ${it.deviceName}") b.targetDeviceDropDown.setText("${it.senderName} (${it.deviceId}) - ${it.deviceName}")
} }
// handle notification intent
arguments?.getString("feedbackMessageDeviceId")?.let { deviceId ->
messages.firstOrNull { it.received && it.deviceId == deviceId && it.devId == null }?.let {
currentDeviceId = deviceId
b.targetDeviceDropDown.setText("${it.senderName} (${it.deviceId}) - ${it.deviceName}")
}
}
b.chatLayout.visibility = View.VISIBLE
b.inputLayout.visibility = View.GONE
}
else if (messages.isEmpty()) {
b.chatLayout.visibility = View.GONE
b.inputLayout.visibility = View.VISIBLE
} }
b.chatLayout.visibility = View.VISIBLE
b.inputLayout.visibility = View.GONE
}
else if (messages.isEmpty()) {
b.chatLayout.visibility = View.GONE
b.inputLayout.visibility = View.VISIBLE
}
loadMessages(messages) loadMessages(messages)
b.sendButton.onClick { b.sendButton.onClick {
send(b.textInput.text.toString()) send(b.textInput.text.toString())
} }
chatView.setOnClickSendButtonListener(View.OnClickListener { chatView.setOnClickSendButtonListener {
send(chatView.inputText) send(chatView.inputText)
})
} }
} }
@ -275,14 +248,4 @@ class FeedbackFragment : Fragment(), CoroutineScope {
private fun openFaq() { private fun openFaq() {
openUrl(activity, "http://szkolny.eu/pomoc/") openUrl(activity, "http://szkolny.eu/pomoc/")
} }
override fun onResume() {
super.onResume()
EventBus.getDefault().register(this)
}
override fun onPause() {
super.onPause()
EventBus.getDefault().unregister(this)
}
} }

View File

@ -13,7 +13,7 @@ import pl.szczodrzynski.edziennik.data.db.full.GradeFull
import pl.szczodrzynski.edziennik.databinding.DialogGradeDetailsBinding import pl.szczodrzynski.edziennik.databinding.DialogGradeDetailsBinding
import pl.szczodrzynski.edziennik.ext.onClick import pl.szczodrzynski.edziennik.ext.onClick
import pl.szczodrzynski.edziennik.ext.setTintColor import pl.szczodrzynski.edziennik.ext.setTintColor
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BindingDialog
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog
import pl.szczodrzynski.edziennik.ui.notes.setupNotesButton import pl.szczodrzynski.edziennik.ui.notes.setupNotesButton
import pl.szczodrzynski.edziennik.utils.BetterLink import pl.szczodrzynski.edziennik.utils.BetterLink

View File

@ -4,50 +4,51 @@
package pl.szczodrzynski.edziennik.ui.grades package pl.szczodrzynski.edziennik.ui.grades
import android.os.AsyncTask
import android.os.Bundle 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.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import kotlinx.coroutines.* import kotlinx.coroutines.Dispatchers
import pl.szczodrzynski.edziennik.* import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.core.manager.GradesManager
import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.enums.MetadataType
import pl.szczodrzynski.edziennik.data.db.full.GradeFull import pl.szczodrzynski.edziennik.data.db.full.GradeFull
import pl.szczodrzynski.edziennik.databinding.GradesListFragmentBinding import pl.szczodrzynski.edziennik.data.enums.MetadataType
import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.data.enums.NavTarget import pl.szczodrzynski.edziennik.data.enums.NavTarget
import pl.szczodrzynski.edziennik.databinding.GradesListFragmentBinding
import pl.szczodrzynski.edziennik.ext.Bundle
import pl.szczodrzynski.edziennik.ext.averageOrNull
import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty
import pl.szczodrzynski.edziennik.ext.startCoroutineTimer
import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog
import pl.szczodrzynski.edziennik.ui.grades.models.GradesAverages import pl.szczodrzynski.edziennik.ui.grades.models.GradesAverages
import pl.szczodrzynski.edziennik.ui.grades.models.GradesSemester import pl.szczodrzynski.edziennik.ui.grades.models.GradesSemester
import pl.szczodrzynski.edziennik.ui.grades.models.GradesStats import pl.szczodrzynski.edziennik.ui.grades.models.GradesStats
import pl.szczodrzynski.edziennik.ui.grades.models.GradesSubject import pl.szczodrzynski.edziennik.ui.grades.models.GradesSubject
import pl.szczodrzynski.edziennik.core.manager.GradesManager
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
import kotlin.coroutines.CoroutineContext
import kotlin.math.max import kotlin.math.max
class GradesListFragment : Fragment(), CoroutineScope { class GradesListFragment : BaseFragment<GradesListFragmentBinding, MainActivity>(
companion object { inflater = GradesListFragmentBinding::inflate,
private const val TAG = "GradesFragment" ) {
}
private lateinit var app: App override fun getMarkAsReadType() = MetadataType.GRADE
private lateinit var activity: MainActivity override fun getBottomSheetItems() = listOf(
private lateinit var b: GradesListFragmentBinding BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_grades_config)
.withIcon(CommunityMaterial.Icon.cmd_cog_outline)
.withOnClickListener {
activity.bottomSheet.close()
GradesConfigDialog(activity, true, null, null).show()
},
)
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local/private variables go here
private val manager private val manager
get() = app.gradesManager get() = app.gradesManager
private val dontCountEnabled private val dontCountEnabled
@ -56,18 +57,7 @@ class GradesListFragment : Fragment(), CoroutineScope {
get() = manager.dontCountGrades get() = manager.dontCountGrades
private var expandSubjectId = 0L private var expandSubjectId = 0L
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override suspend fun onViewReady(savedInstanceState: Bundle?) {
activity = (getActivity() as MainActivity?) ?: return null
context ?: return null
app = activity.application as App
b = GradesListFragmentBinding.inflate(inflater)
b.refreshLayout.setParent(activity.swipeRefreshLayout)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { startCoroutineTimer(100L) {
if (!isAdded) return@startCoroutineTimer
expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L
val adapter = GradesAdapter(activity) val adapter = GradesAdapter(activity)
@ -92,7 +82,6 @@ class GradesListFragment : Fragment(), CoroutineScope {
b.list.apply { b.list.apply {
setHasFixedSize(true) setHasFixedSize(true)
layoutManager = LinearLayoutManager(context) layoutManager = LinearLayoutManager(context)
addOnScrollListener(b.refreshLayout.onScrollListener)
} }
} }
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()
@ -121,7 +110,7 @@ class GradesListFragment : Fragment(), CoroutineScope {
val otherSemester = subject.semesters.firstOrNull { it != semester } val otherSemester = subject.semesters.firstOrNull { it != semester }
var gradeSumOtherSemester = otherSemester?.averages?.normalWeightedSum var gradeSumOtherSemester = otherSemester?.averages?.normalWeightedSum
var gradeCountOtherSemester = otherSemester?.averages?.normalWeightedCount var gradeCountOtherSemester = otherSemester?.averages?.normalWeightedCount
if (gradeSumOtherSemester ?: 0f == 0f || gradeCountOtherSemester ?: 0f == 0f) { if ((gradeSumOtherSemester ?: 0f) == 0f || (gradeCountOtherSemester ?: 0f) == 0f) {
gradeSumOtherSemester = otherSemester?.averages?.normalSum gradeSumOtherSemester = otherSemester?.averages?.normalSum
gradeCountOtherSemester = otherSemester?.averages?.normalCount?.toFloat() gradeCountOtherSemester = otherSemester?.averages?.normalCount?.toFloat()
} }
@ -137,27 +126,7 @@ class GradesListFragment : Fragment(), CoroutineScope {
"finalOtherSemester" to otherSemester?.finalGrade?.value "finalOtherSemester" to otherSemester?.finalGrade?.value
)) ))
} }
}
activity.bottomSheet.prependItems(
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_grades_config)
.withIcon(CommunityMaterial.Icon.cmd_cog_outline)
.withOnClickListener(View.OnClickListener {
activity.bottomSheet.close()
GradesConfigDialog(activity, true, null, null).show()
}),
BottomSheetSeparatorItem(true),
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_mark_as_read)
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
.withOnClickListener(View.OnClickListener {
activity.bottomSheet.close()
AsyncTask.execute { App.db.metadataDao().setAllSeen(App.profileId, MetadataType.GRADE, true) }
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show()
})
)
activity.gainAttention()
}}
private fun expandSubject(adapter: GradesAdapter) { private fun expandSubject(adapter: GradesAdapter) {
var expandSubjectModel: GradesSubject? = null var expandSubjectModel: GradesSubject? = null

View File

@ -4,41 +4,34 @@ import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter import android.graphics.PorterDuffColorFilter
import android.os.Bundle import android.os.Bundle
import android.text.InputType import android.text.InputType
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.core.manager.GradesManager.Companion.YEAR_1_AVG_2_AVG
import pl.szczodrzynski.edziennik.core.manager.GradesManager.Companion.YEAR_1_AVG_2_SEM
import pl.szczodrzynski.edziennik.core.manager.GradesManager.Companion.YEAR_1_SEM_2_AVG
import pl.szczodrzynski.edziennik.core.manager.GradesManager.Companion.YEAR_ALL_GRADES
import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.databinding.FragmentGradesEditorBinding import pl.szczodrzynski.edziennik.databinding.FragmentGradesEditorBinding
import pl.szczodrzynski.edziennik.ext.getFloat import pl.szczodrzynski.edziennik.ext.getFloat
import pl.szczodrzynski.edziennik.ext.getInt import pl.szczodrzynski.edziennik.ext.getInt
import pl.szczodrzynski.edziennik.ext.getLong import pl.szczodrzynski.edziennik.ext.getLong
import pl.szczodrzynski.edziennik.ext.input import pl.szczodrzynski.edziennik.ext.input
import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
import pl.szczodrzynski.edziennik.utils.Colors import pl.szczodrzynski.edziennik.utils.Colors
import pl.szczodrzynski.edziennik.core.manager.GradesManager.Companion.YEAR_1_AVG_2_AVG
import pl.szczodrzynski.edziennik.core.manager.GradesManager.Companion.YEAR_1_AVG_2_SEM
import pl.szczodrzynski.edziennik.core.manager.GradesManager.Companion.YEAR_1_SEM_2_AVG
import pl.szczodrzynski.edziennik.core.manager.GradesManager.Companion.YEAR_ALL_GRADES
import timber.log.Timber import timber.log.Timber
import java.text.DecimalFormat import java.text.DecimalFormat
import java.util.* import java.util.Locale
import kotlin.math.floor import kotlin.math.floor
class GradesEditorFragment : Fragment() { class GradesEditorFragment : BaseFragment<FragmentGradesEditorBinding, MainActivity>(
inflater = FragmentGradesEditorBinding::inflate,
private lateinit var app: App ) {
private lateinit var activity: MainActivity
private lateinit var b: FragmentGradesEditorBinding
/*
private val navController: NavController by lazy { Navigation.findNavController(b.root) }
*/
private val config by lazy { app.profile.config.grades } private val config by lazy { app.profile.config.grades }
@ -61,20 +54,7 @@ class GradesEditorFragment : Fragment() {
private var averageMode = YEAR_ALL_GRADES // this means the container should be gone private var averageMode = YEAR_ALL_GRADES // this means the container should be gone
private var yearAverageBefore = 0.0f private var yearAverageBefore = 0.0f
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override suspend fun onViewReady(savedInstanceState: Bundle?) {
activity = (getActivity() as MainActivity?) ?: return null
if (context == null)
return null
app = activity.application as App
// activity, context and profile is valid
b = FragmentGradesEditorBinding.inflate(inflater)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (!isAdded)
return
subjectId = arguments.getLong("subjectId", -1) subjectId = arguments.getLong("subjectId", -1)
semester = arguments.getInt("semester", 1) semester = arguments.getInt("semester", 1)

View File

@ -5,16 +5,22 @@
package pl.szczodrzynski.edziennik.ui.home package pl.szczodrzynski.edziennik.ui.home
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.ItemTouchHelper.* import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_DRAG
import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_IDLE
import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_SWIPE
import androidx.recyclerview.widget.ItemTouchHelper.DOWN
import androidx.recyclerview.widget.ItemTouchHelper.LEFT
import androidx.recyclerview.widget.ItemTouchHelper.UP
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
import pl.szczodrzynski.edziennik.ui.home.HomeFragment.Companion.removeCard import pl.szczodrzynski.edziennik.ui.home.HomeFragment.Companion.removeCard
import pl.szczodrzynski.edziennik.ui.home.HomeFragment.Companion.swapCards import pl.szczodrzynski.edziennik.ui.home.HomeFragment.Companion.swapCards
import pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator
class CardItemTouchHelperCallback(private val cardAdapter: HomeCardAdapter, private val refreshLayout: SwipeRefreshLayoutNoIndicator?) : ItemTouchHelper.Callback() { class CardItemTouchHelperCallback(
private val cardAdapter: HomeCardAdapter,
private val onCanRefresh: ((canRefresh: Boolean) -> Unit)?,
) : ItemTouchHelper.Callback() {
companion object { companion object {
private const val TAG = "CardItemTouchHelperCallback"
private const val DRAG_FLAGS = UP or DOWN private const val DRAG_FLAGS = UP or DOWN
private const val SWIPE_FLAGS = LEFT private const val SWIPE_FLAGS = LEFT
} }
@ -45,10 +51,10 @@ class CardItemTouchHelperCallback(private val cardAdapter: HomeCardAdapter, priv
if (viewHolder != null && (actionState == ACTION_STATE_DRAG || actionState == ACTION_STATE_SWIPE)) { if (viewHolder != null && (actionState == ACTION_STATE_DRAG || actionState == ACTION_STATE_SWIPE)) {
dragCardView = viewHolder.itemView as MaterialCardView dragCardView = viewHolder.itemView as MaterialCardView
dragCardView?.isDragged = true dragCardView?.isDragged = true
refreshLayout?.isEnabled = false onCanRefresh?.invoke(false)
} }
else if (actionState == ACTION_STATE_IDLE && dragCardView != null) { else if (actionState == ACTION_STATE_IDLE && dragCardView != null) {
refreshLayout?.isEnabled = true onCanRefresh?.invoke(true)
dragCardView?.isDragged = false dragCardView?.isDragged = false
dragCardView = null dragCardView = null
} }

View File

@ -8,7 +8,7 @@ import androidx.appcompat.app.AppCompatActivity
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BaseDialog
import pl.szczodrzynski.edziennik.ui.home.HomeCard.Companion.CARD_EVENTS import pl.szczodrzynski.edziennik.ui.home.HomeCard.Companion.CARD_EVENTS
import pl.szczodrzynski.edziennik.ui.home.HomeCard.Companion.CARD_GRADES import pl.szczodrzynski.edziennik.ui.home.HomeCard.Companion.CARD_GRADES
import pl.szczodrzynski.edziennik.ui.home.HomeCard.Companion.CARD_LUCKY_NUMBER import pl.szczodrzynski.edziennik.ui.home.HomeCard.Companion.CARD_LUCKY_NUMBER

View File

@ -5,22 +5,19 @@
package pl.szczodrzynski.edziennik.ui.home package pl.szczodrzynski.edziennik.ui.home
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.View.OnClickListener
import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.core.view.AccessibilityDelegateCompat import androidx.core.view.AccessibilityDelegateCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
import androidx.core.widget.NestedScrollView import androidx.core.widget.NestedScrollView
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial.Icon import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial.Icon
import eu.szkolny.font.SzkolnyFont import eu.szkolny.font.SzkolnyFont
import kotlinx.coroutines.* import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.BuildConfig import pl.szczodrzynski.edziennik.BuildConfig
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
@ -29,16 +26,22 @@ import pl.szczodrzynski.edziennik.data.enums.FeatureType
import pl.szczodrzynski.edziennik.databinding.FragmentHomeBinding import pl.szczodrzynski.edziennik.databinding.FragmentHomeBinding
import pl.szczodrzynski.edziennik.ext.hasUIFeature import pl.szczodrzynski.edziennik.ext.hasUIFeature
import pl.szczodrzynski.edziennik.ext.onClick import pl.szczodrzynski.edziennik.ext.onClick
import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
import pl.szczodrzynski.edziennik.ui.dialogs.settings.StudentNumberDialog import pl.szczodrzynski.edziennik.ui.dialogs.settings.StudentNumberDialog
import pl.szczodrzynski.edziennik.ui.home.cards.* import pl.szczodrzynski.edziennik.ui.home.cards.HomeArchiveCard
import pl.szczodrzynski.edziennik.ui.home.cards.HomeAvailabilityCard
import pl.szczodrzynski.edziennik.ui.home.cards.HomeEventsCard
import pl.szczodrzynski.edziennik.ui.home.cards.HomeGradesCard
import pl.szczodrzynski.edziennik.ui.home.cards.HomeLuckyNumberCard
import pl.szczodrzynski.edziennik.ui.home.cards.HomeNotesCard
import pl.szczodrzynski.edziennik.ui.home.cards.HomeTimetableCard
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 HomeFragment : Fragment(), CoroutineScope { class HomeFragment : BaseFragment<FragmentHomeBinding, MainActivity>(
inflater = FragmentHomeBinding::inflate,
) {
companion object { companion object {
private const val TAG = "HomeFragment"
fun swapCards(fromPosition: Int, toPosition: Int, cardAdapter: HomeCardAdapter): Boolean { fun swapCards(fromPosition: Int, toPosition: Int, cardAdapter: HomeCardAdapter): Boolean {
val fromCard = cardAdapter.items[fromPosition] val fromCard = cardAdapter.items[fromPosition]
val toCard = cardAdapter.items[toPosition] val toCard = cardAdapter.items[toPosition]
@ -75,75 +78,58 @@ class HomeFragment : Fragment(), CoroutineScope {
} }
} }
private lateinit var app: App override fun getBottomSheetItems() = listOf(
private lateinit var activity: MainActivity BottomSheetPrimaryItem(true)
private lateinit var b: FragmentHomeBinding .withTitle(R.string.menu_add_remove_cards)
.withIcon(Icon.cmd_card_bulleted_settings_outline)
.withOnClickListener {
activity.bottomSheet.close()
HomeConfigDialog(activity, reloadOnDismiss = true).show()
},
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_set_student_number)
.withIcon(SzkolnyFont.Icon.szf_clipboard_list_outline)
.withOnClickListener {
activity.bottomSheet.close()
StudentNumberDialog(activity, app.profile) {
app.profileSave()
}
},
BottomSheetSeparatorItem(true),
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_mark_everything_as_read)
.withIcon(Icon.cmd_eye_check_outline)
.withOnClickListener {
activity.bottomSheet.close()
launch(Dispatchers.IO) {
if (!app.data.uiConfig.enableMarkAsReadAnnouncements) {
app.db.metadataDao()
.setAllSeenExceptMessagesAndAnnouncements(App.profileId, true)
} else {
app.db.metadataDao().setAllSeenExceptMessages(App.profileId, true)
}
}
Toast.makeText(
activity,
R.string.main_menu_mark_as_read_success,
Toast.LENGTH_SHORT
).show()
}
)
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private val manager private val manager
get() = app.permissionManager get() = app.permissionManager
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 = FragmentHomeBinding.inflate(inflater)
b.refreshLayout.setParent(activity.swipeRefreshLayout)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// TODO check if app, activity, b can be null
if (!isAdded)
return
override suspend fun onViewReady(savedInstanceState: Bundle?) {
if (!manager.isNotificationPermissionGranted) { if (!manager.isNotificationPermissionGranted) {
manager.requestNotificationsPermission(activity, 0, false){} manager.requestNotificationsPermission(activity, 0, false){}
} }
activity.bottomSheet.prependItems(
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_add_remove_cards)
.withIcon(Icon.cmd_card_bulleted_settings_outline)
.withOnClickListener(OnClickListener {
activity.bottomSheet.close()
HomeConfigDialog(activity, reloadOnDismiss = true).show()
}),
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_set_student_number)
.withIcon(SzkolnyFont.Icon.szf_clipboard_list_outline)
.withOnClickListener(OnClickListener {
activity.bottomSheet.close()
StudentNumberDialog(activity, app.profile) {
app.profileSave()
}
}),
BottomSheetSeparatorItem(true),
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_mark_everything_as_read)
.withIcon(Icon.cmd_eye_check_outline)
.withOnClickListener(OnClickListener {
activity.bottomSheet.close()
launch { withContext(Dispatchers.Default) {
if (!app.data.uiConfig.enableMarkAsReadAnnouncements) {
app.db.metadataDao().setAllSeenExceptMessagesAndAnnouncements(App.profileId, true)
} else {
app.db.metadataDao().setAllSeenExceptMessages(App.profileId, true)
}
} }
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show()
})
)
b.configureCards.onClick { b.configureCards.onClick {
HomeConfigDialog(activity, reloadOnDismiss = true).show() HomeConfigDialog(activity, reloadOnDismiss = true).show()
} }
b.scrollView.setOnScrollChangeListener { _: NestedScrollView?, _: Int, scrollY: Int, _: Int, _: Int ->
b.refreshLayout.isEnabled = scrollY == 0
}
val cards = app.profile.config.ui.homeCards.filter { it.profileId == app.profile.id }.toMutableList() val cards = app.profile.config.ui.homeCards.filter { it.profileId == app.profile.id }.toMutableList()
if (cards.isEmpty()) { if (cards.isEmpty()) {
cards += listOfNotNull( cards += listOfNotNull(
@ -184,7 +170,9 @@ class HomeFragment : Fragment(), CoroutineScope {
} }
val adapter = HomeCardAdapter(items) val adapter = HomeCardAdapter(items)
val itemTouchHelper = ItemTouchHelper(CardItemTouchHelperCallback(adapter, b.refreshLayout)) val itemTouchHelper = ItemTouchHelper(CardItemTouchHelperCallback(adapter) {
canRefreshDisabled = !it
})
adapter.itemTouchHelper = itemTouchHelper adapter.itemTouchHelper = itemTouchHelper
b.list.layoutManager = LinearLayoutManager(activity) b.list.layoutManager = LinearLayoutManager(activity)
b.list.adapter = adapter b.list.adapter = adapter

View File

@ -4,116 +4,49 @@
package pl.szczodrzynski.edziennik.ui.homework package pl.szczodrzynski.edziennik.ui.homework
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 com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import eu.szkolny.font.SzkolnyFont import eu.szkolny.font.SzkolnyFont
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
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.R import pl.szczodrzynski.edziennik.R
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.enums.MetadataType import pl.szczodrzynski.edziennik.data.enums.MetadataType
import pl.szczodrzynski.edziennik.databinding.HomeworkFragmentBinding import pl.szczodrzynski.edziennik.databinding.BasePagerFragmentBinding
import pl.szczodrzynski.edziennik.ext.Bundle import pl.szczodrzynski.edziennik.ext.Bundle
import pl.szczodrzynski.edziennik.ext.addOnPageSelectedListener import pl.szczodrzynski.edziennik.ui.base.fragment.PagerFragment
import pl.szczodrzynski.edziennik.ui.base.lazypager.FragmentLazyPagerAdapter
import pl.szczodrzynski.edziennik.ui.event.EventManualDialog import pl.szczodrzynski.edziennik.ui.event.EventManualDialog
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
import kotlin.coroutines.CoroutineContext
class HomeworkFragment : Fragment(), CoroutineScope { class HomeworkFragment : PagerFragment<BasePagerFragmentBinding, MainActivity>(
companion object { inflater = BasePagerFragmentBinding::inflate,
private const val TAG = "HomeworkFragment" ), CoroutineScope {
var pageSelection = 0
}
private lateinit var app: App override fun getFab() = R.string.add to CommunityMaterial.Icon3.cmd_plus
private lateinit var activity: MainActivity override fun getMarkAsReadType() = MetadataType.HOMEWORK
private lateinit var b: HomeworkFragmentBinding override fun getBottomSheetItems() = listOf(
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 = HomeworkFragmentBinding.inflate(inflater)
b.refreshLayout.setParent(activity.swipeRefreshLayout)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (!isAdded) return
activity.bottomSheet.prependItems(
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_add_event)
.withDescription(R.string.menu_add_event_desc)
.withIcon(SzkolnyFont.Icon.szf_calendar_plus_outline)
.withOnClickListener(View.OnClickListener {
activity.bottomSheet.close()
EventManualDialog(activity, App.profileId, defaultType = Event.TYPE_HOMEWORK).show()
}),
BottomSheetSeparatorItem(true),
BottomSheetPrimaryItem(true) BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_mark_as_read) .withTitle(R.string.menu_add_event)
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline) .withDescription(R.string.menu_add_event_desc)
.withOnClickListener(View.OnClickListener { .withIcon(SzkolnyFont.Icon.szf_calendar_plus_outline)
activity.bottomSheet.close() .withOnClickListener {
AsyncTask.execute { app.db.metadataDao().setAllSeen(App.profileId, MetadataType.HOMEWORK, true) } activity.bottomSheet.close()
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show()
}))
val pagerAdapter = FragmentLazyPagerAdapter(
parentFragmentManager,
b.refreshLayout,
listOf(
HomeworkListFragment().apply {
arguments = Bundle("homeworkDate" to HomeworkDate.CURRENT)
} to getString(R.string.homework_tab_current),
HomeworkListFragment().apply {
arguments = Bundle("homeworkDate" to HomeworkDate.PAST)
} to getString(R.string.homework_tab_past)
)
)
b.viewPager.apply {
offscreenPageLimit = 1
adapter = pagerAdapter
currentItem = pageSelection
addOnPageSelectedListener {
pageSelection = it
}
b.tabLayout.setupWithViewPager(this)
}
activity.navView.apply {
bottomBar.apply {
fabEnable = true
fabExtendedText = getString(R.string.add)
fabIcon = CommunityMaterial.Icon3.cmd_plus
}
setFabOnClickListener(View.OnClickListener {
EventManualDialog(activity, App.profileId, defaultType = Event.TYPE_HOMEWORK).show() EventManualDialog(activity, App.profileId, defaultType = Event.TYPE_HOMEWORK).show()
}) }
} )
activity.gainAttention() override fun getTabLayout() = b.tabLayout
activity.gainAttentionFAB() override fun getViewPager() = b.viewPager
override suspend fun onCreatePages() = listOf(
HomeworkListFragment().apply {
arguments = Bundle("homeworkDate" to HomeworkDate.CURRENT)
} to getString(R.string.homework_tab_current),
HomeworkListFragment().apply {
arguments = Bundle("homeworkDate" to HomeworkDate.PAST)
} to getString(R.string.homework_tab_past)
)
override suspend fun onFabClick() {
EventManualDialog(activity, App.profileId, defaultType = Event.TYPE_HOMEWORK).show()
} }
} }

View File

@ -1,52 +1,28 @@
package pl.szczodrzynski.edziennik.ui.homework package pl.szczodrzynski.edziennik.ui.homework
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.coroutines.CoroutineScope import pl.szczodrzynski.edziennik.App
import kotlinx.coroutines.Dispatchers import pl.szczodrzynski.edziennik.MainActivity
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.HomeworkListFragmentBinding import pl.szczodrzynski.edziennik.databinding.HomeworkListFragmentBinding
import pl.szczodrzynski.edziennik.ext.getInt import pl.szczodrzynski.edziennik.ext.getInt
import pl.szczodrzynski.edziennik.ext.startCoroutineTimer import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
import pl.szczodrzynski.edziennik.ui.base.lazypager.LazyFragment
import pl.szczodrzynski.edziennik.ui.event.EventDetailsDialog import pl.szczodrzynski.edziennik.ui.event.EventDetailsDialog
import pl.szczodrzynski.edziennik.ui.event.EventListAdapter import pl.szczodrzynski.edziennik.ui.event.EventListAdapter
import pl.szczodrzynski.edziennik.ui.event.EventManualDialog import pl.szczodrzynski.edziennik.ui.event.EventManualDialog
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration 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 : LazyFragment(), CoroutineScope { class HomeworkListFragment : BaseFragment<HomeworkListFragmentBinding, MainActivity>(
companion object { inflater = HomeworkListFragmentBinding::inflate,
private const val TAG = "HomeworkListFragment" ) {
}
private lateinit var app: App override fun getRefreshScrollingView() = b.list
private lateinit var activity: MainActivity
private lateinit var b: HomeworkListFragmentBinding
private val job: Job = Job() override suspend fun onViewReady(savedInstanceState: Bundle?) {
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 = HomeworkListFragmentBinding.inflate(inflater)
return b.root
}
override fun onPageCreated(): Boolean { startCoroutineTimer(100L) {
val homeworkDate = arguments.getInt("homeworkDate", HomeworkDate.CURRENT) val homeworkDate = arguments.getInt("homeworkDate", HomeworkDate.CURRENT)
val today = Date.getToday() val today = Date.getToday()
@ -87,7 +63,6 @@ class HomeworkListFragment : LazyFragment(), CoroutineScope {
} }
// show/hide relevant views // show/hide relevant views
setSwipeToRefresh(events.isEmpty())
b.progressBar.isVisible = false b.progressBar.isVisible = false
b.list.isVisible = events.isNotEmpty() b.list.isVisible = events.isNotEmpty()
b.noData.isVisible = events.isEmpty() b.noData.isVisible = events.isEmpty()
@ -104,7 +79,6 @@ class HomeworkListFragment : LazyFragment(), CoroutineScope {
setHasFixedSize(true) setHasFixedSize(true)
layoutManager = LinearLayoutManager(context) layoutManager = LinearLayoutManager(context)
addItemDecoration(SimpleDividerItemDecoration(context)) addItemDecoration(SimpleDividerItemDecoration(context))
addOnScrollListener(onScrollListener)
this.adapter = adapter this.adapter = adapter
} }
} }
@ -112,5 +86,5 @@ class HomeworkListFragment : LazyFragment(), CoroutineScope {
// reapply the filter // reapply the filter
adapter.getSearchField()?.applyTo(adapter) adapter.getSearchField()?.applyTo(adapter)
}) })
}; return true } }
} }

View File

@ -9,6 +9,7 @@ import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.NavOptions import androidx.navigation.NavOptions
import androidx.navigation.Navigation import androidx.navigation.Navigation
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -19,8 +20,7 @@ import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.databinding.LoginActivityBinding import pl.szczodrzynski.edziennik.databinding.LoginActivityBinding
import pl.szczodrzynski.edziennik.ui.error.ErrorSnackbar import pl.szczodrzynski.edziennik.ui.main.ErrorSnackbar
import pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoTouch
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class LoginActivity : AppCompatActivity(), CoroutineScope { class LoginActivity : AppCompatActivity(), CoroutineScope {
@ -34,7 +34,7 @@ class LoginActivity : AppCompatActivity(), CoroutineScope {
lateinit var navOptionsBuilder: NavOptions.Builder lateinit var navOptionsBuilder: NavOptions.Builder
val nav by lazy { Navigation.findNavController(this, R.id.nav_host_fragment) } val nav by lazy { Navigation.findNavController(this, R.id.nav_host_fragment) }
val errorSnackbar: ErrorSnackbar by lazy { ErrorSnackbar(this) } val errorSnackbar: ErrorSnackbar by lazy { ErrorSnackbar(this) }
val swipeRefreshLayout: SwipeRefreshLayoutNoTouch by lazy { b.swipeRefreshLayout } val swipeRefreshLayout: SwipeRefreshLayout by lazy { b.swipeRefreshLayout }
private val job: Job = Job() private val job: Job = Job()
override val coroutineContext: CoroutineContext override val coroutineContext: CoroutineContext

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2021-3-27. * Copyright (c) Kuba Szczodrzyński 2021-3-27.
*/ */
package pl.szczodrzynski.edziennik.ui.base package pl.szczodrzynski.edziennik.ui.main
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
@ -12,7 +12,6 @@ import com.mikepenz.iconics.utils.colorInt
import pl.szczodrzynski.edziennik.databinding.ActivityBuildInvalidBinding import pl.szczodrzynski.edziennik.databinding.ActivityBuildInvalidBinding
import pl.szczodrzynski.edziennik.ext.app import pl.szczodrzynski.edziennik.ext.app
import pl.szczodrzynski.edziennik.ext.onClick import pl.szczodrzynski.edziennik.ext.onClick
import pl.szczodrzynski.edziennik.utils.Themes
class BuildInvalidActivity : AppCompatActivity() { class BuildInvalidActivity : AppCompatActivity() {

View File

@ -1,4 +1,4 @@
package pl.szczodrzynski.edziennik.ui.base package pl.szczodrzynski.edziennik.ui.main
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
@ -21,6 +21,7 @@ import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.data.api.szkolny.request.ErrorReportRequest import pl.szczodrzynski.edziennik.data.api.szkolny.request.ErrorReportRequest
import pl.szczodrzynski.edziennik.ext.resolveAttr import pl.szczodrzynski.edziennik.ext.resolveAttr
import pl.szczodrzynski.edziennik.ext.resolveColor import pl.szczodrzynski.edziennik.ext.resolveColor
import pl.szczodrzynski.edziennik.ext.resolveStyleAttr
import pl.szczodrzynski.edziennik.utils.html.BetterHtml import pl.szczodrzynski.edziennik.utils.html.BetterHtml
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
@ -88,7 +89,7 @@ class CrashActivity : AppCompatActivity(), CoroutineScope {
val moreInfoButton = findViewById<Button>(R.id.crash_details_btn) val moreInfoButton = findViewById<Button>(R.id.crash_details_btn)
moreInfoButton.setOnClickListener { moreInfoButton.setOnClickListener {
MaterialAlertDialogBuilder(this, R.attr.materialAlertDialogMonospaceTheme.resolveAttr(this)) MaterialAlertDialogBuilder(this, R.attr.materialAlertDialogMonospaceTheme.resolveStyleAttr(this))
.setTitle(R.string.crash_details) .setTitle(R.string.crash_details)
.setMessage(BetterHtml.fromHtml(context = null, getErrorString(intent, false))) .setMessage(BetterHtml.fromHtml(context = null, getErrorString(intent, false)))
.setPositiveButton(R.string.close, null) .setPositiveButton(R.string.close, null)

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2019-11-13. * Copyright (c) Kuba Szczodrzyński 2019-11-13.
*/ */
package pl.szczodrzynski.edziennik.ui.error package pl.szczodrzynski.edziennik.ui.main
import android.view.View import android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@ -14,6 +14,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.ui.dialogs.ErrorDetailsDialog
import pl.szczodrzynski.navlib.getColorFromAttr import pl.szczodrzynski.navlib.getColorFromAttr
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2019-12-22. * Copyright (c) Kuba Szczodrzyński 2019-12-22.
*/ */
package pl.szczodrzynski.edziennik.ui.base package pl.szczodrzynski.edziennik.ui.main
import android.view.View import android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity

View File

@ -6,22 +6,27 @@ package pl.szczodrzynski.edziennik.ui.messages.compose
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import android.widget.AutoCompleteTextView import android.widget.AutoCompleteTextView
import android.widget.ScrollView import android.widget.ScrollView
import android.widget.Toast import android.widget.Toast
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import kotlinx.coroutines.* import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.core.manager.MessageManager.UIConfig
import pl.szczodrzynski.edziennik.core.manager.TextStylingManager.HtmlMode.COMPATIBLE
import pl.szczodrzynski.edziennik.core.manager.TextStylingManager.HtmlMode.ORIGINAL
import pl.szczodrzynski.edziennik.core.manager.TextStylingManager.StylingConfig
import pl.szczodrzynski.edziennik.data.api.ERROR_MESSAGE_NOT_SENT import pl.szczodrzynski.edziennik.data.api.ERROR_MESSAGE_NOT_SENT
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.events.MessageSentEvent import pl.szczodrzynski.edziennik.data.api.events.MessageSentEvent
@ -30,33 +35,49 @@ import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.enums.LoginType import pl.szczodrzynski.edziennik.data.enums.LoginType
import pl.szczodrzynski.edziennik.data.enums.NavTarget
import pl.szczodrzynski.edziennik.databinding.MessagesComposeFragmentBinding import pl.szczodrzynski.edziennik.databinding.MessagesComposeFragmentBinding
import pl.szczodrzynski.edziennik.ext.Bundle import pl.szczodrzynski.edziennik.ext.Bundle
import pl.szczodrzynski.edziennik.ext.DAY import pl.szczodrzynski.edziennik.ext.DAY
import pl.szczodrzynski.edziennik.data.enums.NavTarget import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
import pl.szczodrzynski.edziennik.ui.dialogs.settings.MessagesConfigDialog import pl.szczodrzynski.edziennik.ui.dialogs.settings.MessagesConfigDialog
import pl.szczodrzynski.edziennik.ui.messages.list.MessagesFragment import pl.szczodrzynski.edziennik.ui.messages.list.MessagesFragment
import pl.szczodrzynski.edziennik.utils.DefaultTextStyles import pl.szczodrzynski.edziennik.utils.DefaultTextStyles
import pl.szczodrzynski.edziennik.core.manager.MessageManager.UIConfig
import pl.szczodrzynski.edziennik.core.manager.TextStylingManager.HtmlMode.COMPATIBLE
import pl.szczodrzynski.edziennik.core.manager.TextStylingManager.HtmlMode.ORIGINAL
import pl.szczodrzynski.edziennik.core.manager.TextStylingManager.StylingConfig
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 MessagesComposeFragment : Fragment(), CoroutineScope { class MessagesComposeFragment : BaseFragment<MessagesComposeFragmentBinding, MainActivity>(
inflater = MessagesComposeFragmentBinding::inflate,
) {
companion object { companion object {
private const val TAG = "MessagesComposeFragment" private const val TAG = "MessagesComposeFragment"
} }
private lateinit var app: App override fun getFab() = R.string.messages_compose_send to CommunityMaterial.Icon3.cmd_send_outline
private lateinit var activity: MainActivity override fun getBottomSheetItems() = listOf(
private lateinit var b: MessagesComposeFragmentBinding BottomSheetPrimaryItem(true)
.withTitle(R.string.messages_compose_send_long)
private val job: Job = Job() .withIcon(CommunityMaterial.Icon3.cmd_send_outline)
override val coroutineContext: CoroutineContext .withOnClickListener {
get() = job + Dispatchers.Main activity.bottomSheet.close()
sendMessage()
},
BottomSheetPrimaryItem(true)
.withTitle(R.string.messages_compose_save_draft)
.withIcon(CommunityMaterial.Icon.cmd_content_save_edit_outline)
.withOnClickListener {
activity.bottomSheet.close()
saveDraft()
},
BottomSheetSeparatorItem(true),
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_messages_config)
.withIcon(CommunityMaterial.Icon.cmd_cog_outline)
.withOnClickListener {
activity.bottomSheet.close()
MessagesConfigDialog(activity, false, null, null).show()
}
)
private val manager private val manager
get() = app.messageManager get() = app.messageManager
@ -75,25 +96,7 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
private var discardDraftItem: BottomSheetPrimaryItem? = null private var discardDraftItem: BottomSheetPrimaryItem? = null
private var draftMessageId: Long? = null private var draftMessageId: Long? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override suspend fun onViewReady(savedInstanceState: Bundle?) {
activity = (getActivity() as MainActivity?) ?: return null
context ?: return null
app = activity.application as App
// activity, context and profile is valid
b = MessagesComposeFragmentBinding.inflate(inflater)
return b.root
}
override fun onDestroy() {
EventBus.getDefault().unregister(this)
super.onDestroy()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// TODO check if app, activity, b can be null
if (!isAdded)
return
EventBus.getDefault().register(this)
b.breakpoints.visibility = if (App.devMode) View.VISIBLE else View.GONE b.breakpoints.visibility = if (App.devMode) View.VISIBLE else View.GONE
b.breakpoints.setOnClickListener { b.breakpoints.setOnClickListener {
b.breakpoints.isEnabled = true b.breakpoints.isEnabled = true
@ -110,37 +113,8 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
discardDraftDialog() discardDraftDialog()
} }
activity.bottomSheet.prependItems( getRecipientList()
BottomSheetPrimaryItem(true) createView()
.withTitle(R.string.messages_compose_send_long)
.withIcon(CommunityMaterial.Icon3.cmd_send_outline)
.withOnClickListener {
activity.bottomSheet.close()
sendMessage()
},
BottomSheetPrimaryItem(true)
.withTitle(R.string.messages_compose_save_draft)
.withIcon(CommunityMaterial.Icon.cmd_content_save_edit_outline)
.withOnClickListener {
activity.bottomSheet.close()
saveDraft()
},
BottomSheetSeparatorItem(true),
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_messages_config)
.withIcon(CommunityMaterial.Icon.cmd_cog_outline)
.withOnClickListener {
activity.bottomSheet.close()
MessagesConfigDialog(activity, false, null, null).show()
}
)
launch {
delay(100)
getRecipientList()
createView()
}
} }
private fun getMessageBody(): String { private fun getMessageBody(): String {
@ -150,18 +124,16 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
b.text.text?.toString() ?: "" b.text.text?.toString() ?: ""
} }
private fun getRecipientList() { private suspend fun getRecipientList() {
if (app.data.messagesConfig.syncRecipientList && System.currentTimeMillis() - app.profile.lastReceiversSync > 1 * DAY * 1000) { if (app.data.messagesConfig.syncRecipientList && System.currentTimeMillis() - app.profile.lastReceiversSync > 1 * DAY * 1000) {
activity.snackbar("Pobieranie listy odbiorców...") activity.snackbar("Pobieranie listy odbiorców...")
EdziennikTask.recipientListGet(App.profileId).enqueue(activity) EdziennikTask.recipientListGet(App.profileId).enqueue(activity)
} }
else { else {
launch { val list = withContext(Dispatchers.IO) {
val list = withContext(Dispatchers.Default) { app.db.teacherDao().getAllNow(App.profileId).filter { it.loginId != null }
app.db.teacherDao().getAllNow(App.profileId).filter { it.loginId != null }
}
updateRecipientList(list)
} }
updateRecipientList(list)
} }
} }
@ -259,18 +231,10 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
changedBody = true changedBody = true
} }
} }
}
activity.navView.bottomBar.apply { override suspend fun onFabClick() {
fabEnable = true sendMessage()
fabExtendedText = getString(R.string.messages_compose_send)
fabIcon = CommunityMaterial.Icon3.cmd_send_outline
setFabOnClickListener {
sendMessage()
}
}
activity.gainAttentionFAB()
} }
private fun onBeforeNavigate(): Boolean { private fun onBeforeNavigate(): Boolean {
@ -437,15 +401,13 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
if (!isAdded || !this::activity.isInitialized) if (!isAdded)
return return
activity.onBeforeNavigate = this::onBeforeNavigate activity.onBeforeNavigate = this::onBeforeNavigate
} }
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
if (!this::activity.isInitialized)
return
activity.onBeforeNavigate = null activity.onBeforeNavigate = null
} }

View File

@ -1,25 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-4-4.
*/
package pl.szczodrzynski.edziennik.ui.messages.compose
class MessagesComposeInfo(
/**
* 0 means no attachments.
* -1 means unlimited number.
*/
@JvmField var maxAttachmentNumber: Int,
/**
* -1 means unlimited size.
*/
var attachmentSizeLimit: Long,
/**
* -1 means unlimited length.
*/
var maxSubjectLength: Int,
/**
* -1 means unlimited length.
*/
var maxBodyLength: Int
)

View File

@ -1,144 +1,72 @@
package pl.szczodrzynski.edziennik.ui.messages.list package pl.szczodrzynski.edziennik.ui.messages.list
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
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.Message import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.databinding.MessagesFragmentBinding
import pl.szczodrzynski.edziennik.ext.Bundle
import pl.szczodrzynski.edziennik.ext.addOnPageSelectedListener
import pl.szczodrzynski.edziennik.data.enums.NavTarget import pl.szczodrzynski.edziennik.data.enums.NavTarget
import pl.szczodrzynski.edziennik.ui.base.lazypager.FragmentLazyPagerAdapter import pl.szczodrzynski.edziennik.databinding.BasePagerFragmentBinding
import pl.szczodrzynski.edziennik.ext.Bundle
import pl.szczodrzynski.edziennik.ui.base.fragment.PagerFragment
import pl.szczodrzynski.edziennik.ui.dialogs.settings.MessagesConfigDialog import pl.szczodrzynski.edziennik.ui.dialogs.settings.MessagesConfigDialog
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
import kotlin.coroutines.CoroutineContext
class MessagesFragment : Fragment(), CoroutineScope { class MessagesFragment : PagerFragment<BasePagerFragmentBinding, MainActivity>(
inflater = BasePagerFragmentBinding::inflate,
) {
companion object { companion object {
private const val TAG = "MessagesFragment"
var pageSelection = 0 var pageSelection = 0
} }
private lateinit var app: App override fun getFab() = R.string.compose to CommunityMaterial.Icon3.cmd_pencil_outline
private lateinit var activity: MainActivity override fun getBottomSheetItems() = listOf(
private lateinit var b: MessagesFragmentBinding BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_message_compose)
.withIcon(CommunityMaterial.Icon3.cmd_pencil_outline)
.withOnClickListener {
activity.bottomSheet.close()
activity.navigate(navTarget = NavTarget.MESSAGE_COMPOSE)
},
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_messages_config)
.withIcon(CommunityMaterial.Icon.cmd_cog_outline)
.withOnClickListener {
activity.bottomSheet.close()
MessagesConfigDialog(activity, false, null, null).show()
}
)
private val job: Job = Job() override fun getTabLayout() = b.tabLayout
override val coroutineContext: CoroutineContext override fun getViewPager() = b.viewPager
get() = job + Dispatchers.Main override suspend fun onCreatePages() = listOf(
MessagesListFragment().apply {
// local/private variables go here arguments = Bundle("messageType" to Message.TYPE_RECEIVED)
} to getString(R.string.messages_tab_received),
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { MessagesListFragment().apply {
activity = (getActivity() as MainActivity?) ?: return null arguments = Bundle("messageType" to Message.TYPE_SENT)
context ?: return null } to getString(R.string.messages_tab_sent),
app = activity.application as App MessagesListFragment().apply {
b = MessagesFragmentBinding.inflate(inflater) arguments = Bundle("messageType" to Message.TYPE_DELETED)
b.refreshLayout.setParent(activity.swipeRefreshLayout) } to getString(R.string.messages_tab_deleted),
return b.root MessagesListFragment().apply {
} arguments = Bundle("messageType" to Message.TYPE_DRAFT)
} to getString(R.string.messages_tab_draft),
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { )
if (!isAdded) return
override suspend fun onViewReady(savedInstanceState: Bundle?) {
val messageId = arguments?.getLong("messageId", -1L) ?: -1L val messageId = arguments?.getLong("messageId", -1L) ?: -1L
if (messageId != -1L) { if (messageId != -1L) {
val args = Bundle() val args = Bundle()
args.putLong("messageId", messageId) args.putLong("messageId", messageId)
arguments?.remove("messageId") arguments?.remove("messageId")
activity.navigate(navTarget = NavTarget.MESSAGE, args = args) activity.navigate(navTarget = NavTarget.MESSAGE, args = args)
return
} }
val args = arguments super.onViewReady(savedInstanceState)
val pagerAdapter = FragmentLazyPagerAdapter(
fragmentManager = parentFragmentManager,
swipeRefreshLayout = b.refreshLayout,
fragments = listOf(
MessagesListFragment().apply {
onPageDestroy = this@MessagesFragment.onPageDestroy
arguments = Bundle("messageType" to Message.TYPE_RECEIVED)
args?.getBundle("page0")?.let {
arguments?.putAll(it)
}
} to getString(R.string.messages_tab_received),
MessagesListFragment().apply {
onPageDestroy = this@MessagesFragment.onPageDestroy
arguments = Bundle("messageType" to Message.TYPE_SENT)
args?.getBundle("page1")?.let {
arguments?.putAll(it)
}
} to getString(R.string.messages_tab_sent),
MessagesListFragment().apply {
onPageDestroy = this@MessagesFragment.onPageDestroy
arguments = Bundle("messageType" to Message.TYPE_DELETED)
args?.getBundle("page2")?.let {
arguments?.putAll(it)
}
} to getString(R.string.messages_tab_deleted),
MessagesListFragment().apply {
onPageDestroy = this@MessagesFragment.onPageDestroy
arguments = Bundle("messageType" to Message.TYPE_DRAFT)
args?.getBundle("page3")?.let {
arguments?.putAll(it)
}
} to getString(R.string.messages_tab_draft),
),
)
b.viewPager.apply {
offscreenPageLimit = 1
adapter = pagerAdapter
currentItem = pageSelection
addOnPageSelectedListener {
pageSelection = it
}
b.tabLayout.setupWithViewPager(this)
}
activity.navView.apply {
bottomBar.apply {
fabEnable = true
fabExtendedText = getString(R.string.compose)
fabIcon = CommunityMaterial.Icon3.cmd_pencil_outline
}
bottomSheet.prependItem(
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_messages_config)
.withIcon(CommunityMaterial.Icon.cmd_cog_outline)
.withOnClickListener {
activity.bottomSheet.close()
MessagesConfigDialog(activity, false, null, null).show()
}
)
setFabOnClickListener {
activity.navigate(navTarget = NavTarget.MESSAGE_COMPOSE)
}
}
activity.gainAttentionFAB()
} }
private val onPageDestroy = { position: Int, outState: Bundle? -> override suspend fun onFabClick() {
arguments?.putBundle("page$position", outState) activity.navigate(navTarget = NavTarget.MESSAGE_COMPOSE)
}
override fun onDestroy() {
super.onDestroy()
(b.viewPager.adapter as? FragmentLazyPagerAdapter)?.fragments?.forEach {
it.first.onDestroy()
}
} }
} }

View File

@ -4,58 +4,42 @@
package pl.szczodrzynski.edziennik.ui.messages.list package pl.szczodrzynski.edziennik.ui.messages.list
import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.coroutines.* import kotlinx.coroutines.Dispatchers
import pl.szczodrzynski.edziennik.* import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.enums.NavTarget
import pl.szczodrzynski.edziennik.databinding.MessagesListFragmentBinding import pl.szczodrzynski.edziennik.databinding.MessagesListFragmentBinding
import pl.szczodrzynski.edziennik.ext.Bundle import pl.szczodrzynski.edziennik.ext.Bundle
import pl.szczodrzynski.edziennik.ext.getInt import pl.szczodrzynski.edziennik.ext.getInt
import pl.szczodrzynski.edziennik.ext.startCoroutineTimer import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
import pl.szczodrzynski.edziennik.data.enums.NavTarget
import pl.szczodrzynski.edziennik.ui.base.lazypager.LazyFragment
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import kotlin.coroutines.CoroutineContext
class MessagesListFragment : LazyFragment(), CoroutineScope { class MessagesListFragment : BaseFragment<MessagesListFragmentBinding, MainActivity>(
companion object { inflater = MessagesListFragmentBinding::inflate,
private const val TAG = "MessagesListFragment" ) {
}
override fun getRefreshScrollingView() = b.list
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: MessagesListFragmentBinding
private lateinit var adapter: MessagesAdapter private lateinit var adapter: MessagesAdapter
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local/private variables go here
private val manager private val manager
get() = app.messageManager get() = app.messageManager
var teachers = listOf<Teacher>() var teachers = listOf<Teacher>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @SuppressLint("RestrictedApi")
activity = (getActivity() as MainActivity?) ?: return null override suspend fun onViewReady(savedInstanceState: Bundle?) {
context ?: return null
app = activity.application as App
b = MessagesListFragmentBinding.inflate(inflater)
return b.root
}
override fun onPageCreated(): Boolean { startCoroutineTimer(100L) {
val messageType = arguments.getInt("messageType", Message.TYPE_RECEIVED) val messageType = arguments.getInt("messageType", Message.TYPE_RECEIVED)
var recyclerViewState = var recyclerViewState =
arguments?.getParcelable<LinearLayoutManager.SavedState>("recyclerViewState") savedInstanceState?.getParcelable<LinearLayoutManager.SavedState>("recyclerViewState")
val searchText = arguments?.getString("searchText") val searchText = savedInstanceState?.getString("searchText")
teachers = withContext(Dispatchers.Default) { teachers = withContext(Dispatchers.Default) {
app.db.teacherDao().getAllNow(App.profileId) app.db.teacherDao().getAllNow(App.profileId)
@ -91,8 +75,10 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
} }
} }
if (messageType != Message.TYPE_RECEIVED && messageType != Message.TYPE_SENT)
canRefreshDisabled = true
// show/hide relevant views // show/hide relevant views
setSwipeToRefresh(messageType in Message.TYPE_RECEIVED..Message.TYPE_SENT)
b.progressBar.isVisible = false b.progressBar.isVisible = false
b.list.isVisible = messages.isNotEmpty() b.list.isVisible = messages.isNotEmpty()
b.noData.isVisible = messages.isEmpty() b.noData.isVisible = messages.isEmpty()
@ -109,8 +95,6 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
setHasFixedSize(true) setHasFixedSize(true)
layoutManager = LinearLayoutManager(context) layoutManager = LinearLayoutManager(context)
addItemDecoration(SimpleDividerItemDecoration(context)) addItemDecoration(SimpleDividerItemDecoration(context))
if (messageType in Message.TYPE_RECEIVED..Message.TYPE_SENT)
addOnScrollListener(onScrollListener)
this.adapter = this@MessagesListFragment.adapter this.adapter = this@MessagesListFragment.adapter
} }
} }
@ -126,18 +110,15 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
recyclerViewState = null recyclerViewState = null
} }
}) })
}; return true } }
override fun onDestroy() { override fun onSaveInstanceState(outState: Bundle) {
super.onDestroy() super.onSaveInstanceState(outState)
if (!isAdded || !this::adapter.isInitialized) if (!isAdded || !this::adapter.isInitialized)
return return
val layoutManager = (b.list.layoutManager as? LinearLayoutManager) val layoutManager = (b.list.layoutManager as? LinearLayoutManager)
val searchField = adapter.getSearchField() val searchField = adapter.getSearchField()
outState.putParcelable("recyclerViewState", layoutManager?.onSaveInstanceState())
onPageDestroy?.invoke(position, Bundle( outState.putString("searchText", searchField?.searchText?.toString())
"recyclerViewState" to layoutManager?.onSaveInstanceState(),
"searchText" to searchField?.searchText?.toString()
))
} }
} }

View File

@ -5,28 +5,30 @@
package pl.szczodrzynski.edziennik.ui.messages.single package pl.szczodrzynski.edziennik.ui.messages.single
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import com.mikepenz.iconics.utils.sizeDp import kotlinx.coroutines.launch
import kotlinx.coroutines.*
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
import pl.szczodrzynski.edziennik.data.enums.LoginType
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.databinding.MessageFragmentBinding import pl.szczodrzynski.edziennik.data.enums.LoginType
import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.data.enums.NavTarget import pl.szczodrzynski.edziennik.data.enums.NavTarget
import pl.szczodrzynski.edziennik.databinding.MessageFragmentBinding
import pl.szczodrzynski.edziennik.ext.Bundle
import pl.szczodrzynski.edziennik.ext.attachToastHint
import pl.szczodrzynski.edziennik.ext.get
import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty
import pl.szczodrzynski.edziennik.ext.onClick
import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
import pl.szczodrzynski.edziennik.ui.dialogs.settings.MessagesConfigDialog import pl.szczodrzynski.edziennik.ui.dialogs.settings.MessagesConfigDialog
import pl.szczodrzynski.edziennik.ui.messages.MessagesUtils import pl.szczodrzynski.edziennik.ui.messages.MessagesUtils
import pl.szczodrzynski.edziennik.ui.messages.list.MessagesFragment import pl.szczodrzynski.edziennik.ui.messages.list.MessagesFragment
@ -37,48 +39,27 @@ import pl.szczodrzynski.edziennik.utils.html.BetterHtml
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
import pl.szczodrzynski.navlib.colorAttr
import kotlin.coroutines.CoroutineContext
import kotlin.math.min import kotlin.math.min
class MessageFragment : Fragment(), CoroutineScope { class MessageFragment : BaseFragment<MessageFragmentBinding, MainActivity>(
companion object { inflater = MessageFragmentBinding::inflate,
private const val TAG = "MessageFragment" ) {
}
private lateinit var app: App override fun getBottomSheetItems() = listOf(
private lateinit var activity: MainActivity BottomSheetPrimaryItem(true)
private lateinit var b: MessageFragmentBinding .withTitle(R.string.menu_messages_config)
.withIcon(CommunityMaterial.Icon.cmd_cog_outline)
private val job: Job = Job() .withOnClickListener {
override val coroutineContext: CoroutineContext activity.bottomSheet.close()
get() = job + Dispatchers.Main MessagesConfigDialog(activity, false, null, null).show()
}
)
private val manager private val manager
get() = app.messageManager get() = app.messageManager
private lateinit var message: MessageFull private lateinit var message: MessageFull
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override suspend fun onViewReady(savedInstanceState: Bundle?) {
activity = (getActivity() as MainActivity?) ?: return null
context ?: return null
app = activity.application as App
b = MessageFragmentBinding.inflate(inflater)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (!isAdded) return
activity.bottomSheet.prependItem(
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_messages_config)
.withIcon(CommunityMaterial.Icon.cmd_cog_outline)
.withOnClickListener {
activity.bottomSheet.close()
MessagesConfigDialog(activity, false, null, null).show()
}
)
b.closeButton.onClick { activity.navigateUp() } b.closeButton.onClick { activity.navigateUp() }
// click to expand subject and sender // click to expand subject and sender
@ -88,7 +69,6 @@ class MessageFragment : Fragment(), CoroutineScope {
b.senderContainer.onClick { b.senderContainer.onClick {
b.sender.maxLines = if (b.sender.maxLines == 30) 2 else 30 b.sender.maxLines = if (b.sender.maxLines == 30) 2 else 30
} }
// TODO bring back iconics to reply/forward buttons - add modern icons to SzkolnyFont
b.messageStar.onClick { b.messageStar.onClick {
launch { launch {
@ -129,14 +109,12 @@ class MessageFragment : Fragment(), CoroutineScope {
EdziennikTask.messageGet(App.profileId, message).enqueue(activity) EdziennikTask.messageGet(App.profileId, message).enqueue(activity)
} }
launch { message = manager.getMessage(App.profileId, arguments) ?: run {
message = manager.getMessage(App.profileId, arguments) ?: run { activity.navigateUp()
activity.navigateUp() return
return@launch
}
b.subject.text = message.subject
checkMessage()
} }
b.subject.text = message.subject
checkMessage()
} }
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true) @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
@ -253,17 +231,4 @@ class MessageFragment : Fragment(), CoroutineScope {
}, owner = message) }, owner = message)
} }
} }
override fun onStart() {
EventBus.getDefault().register(this)
super.onStart()
}
override fun onStop() {
super.onStop()
EventBus.getDefault().unregister(this)
}
override fun onDestroy() {
super.onDestroy()
job.cancel()
}
} }

View File

@ -13,7 +13,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Note
import pl.szczodrzynski.edziennik.data.db.entity.Noteable import pl.szczodrzynski.edziennik.data.db.entity.Noteable
import pl.szczodrzynski.edziennik.databinding.NoteDetailsDialogBinding import pl.szczodrzynski.edziennik.databinding.NoteDetailsDialogBinding
import pl.szczodrzynski.edziennik.ext.* import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BindingDialog
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class NoteDetailsDialog( class NoteDetailsDialog(

View File

@ -21,7 +21,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.databinding.NoteEditorDialogBinding import pl.szczodrzynski.edziennik.databinding.NoteEditorDialogBinding
import pl.szczodrzynski.edziennik.ext.isNotNullNorBlank import pl.szczodrzynski.edziennik.ext.isNotNullNorBlank
import pl.szczodrzynski.edziennik.ext.resolveString import pl.szczodrzynski.edziennik.ext.resolveString
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BindingDialog
import pl.szczodrzynski.edziennik.ui.dialogs.settings.RegistrationConfigDialog import pl.szczodrzynski.edziennik.ui.dialogs.settings.RegistrationConfigDialog
import pl.szczodrzynski.edziennik.utils.TextInputDropDown import pl.szczodrzynski.edziennik.utils.TextInputDropDown
import pl.szczodrzynski.edziennik.core.manager.TextStylingManager.HtmlMode import pl.szczodrzynski.edziennik.core.manager.TextStylingManager.HtmlMode

View File

@ -11,7 +11,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Noteable import pl.szczodrzynski.edziennik.data.db.entity.Noteable
import pl.szczodrzynski.edziennik.databinding.NoteListDialogBinding import pl.szczodrzynski.edziennik.databinding.NoteListDialogBinding
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog import pl.szczodrzynski.edziennik.ui.base.dialog.BindingDialog
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
class NoteListDialog( class NoteListDialog(

View File

@ -5,99 +5,32 @@
package pl.szczodrzynski.edziennik.ui.notes package pl.szczodrzynski.edziennik.ui.notes
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import kotlinx.coroutines.* import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Note import pl.szczodrzynski.edziennik.data.db.entity.Note
import pl.szczodrzynski.edziennik.data.db.entity.Noteable import pl.szczodrzynski.edziennik.data.db.entity.Noteable
import pl.szczodrzynski.edziennik.databinding.NotesFragmentBinding import pl.szczodrzynski.edziennik.databinding.NotesFragmentBinding
import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import kotlin.coroutines.CoroutineContext
class NotesFragment : Fragment(), CoroutineScope { class NotesFragment : BaseFragment<NotesFragmentBinding, MainActivity>(
companion object { inflater = NotesFragmentBinding::inflate,
private const val TAG = "NotesFragment" ) {
}
private lateinit var app: App override fun getFab() =
private lateinit var activity: MainActivity R.string.notes_action_add to CommunityMaterial.Icon3.cmd_text_box_plus_outline
private lateinit var b: NotesFragmentBinding
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private val manager private val manager
get() = app.noteManager get() = app.noteManager
override fun onCreateView( override suspend fun onViewReady(savedInstanceState: Bundle?) {
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
activity = getActivity() as? MainActivity ?: return null
context ?: return null
app = activity.application as App
b = NotesFragmentBinding.inflate(inflater)
return b.root
}
private fun onNoteClick(note: Note) = launch {
val owner = withContext(Dispatchers.IO) {
manager.getOwner(note)
} as? Noteable
NoteDetailsDialog(
activity = activity,
owner = owner,
note = note,
).show()
}
private fun onNoteEditClick(note: Note) = launch {
val owner = withContext(Dispatchers.IO) {
manager.getOwner(note)
} as? Noteable
NoteEditorDialog(
activity = activity,
owner = owner,
editingNote = note,
profileId = App.profileId,
).show()
}
private fun onNoteAddClick(view: View?) {
NoteEditorDialog(
activity = activity,
owner = null,
editingNote = null,
profileId = App.profileId,
).show()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (!isAdded) return
activity.navView.apply {
bottomBar.apply {
fabEnable = true
fabExtendedText = getString(R.string.notes_action_add)
fabIcon = CommunityMaterial.Icon3.cmd_text_box_plus_outline
}
setFabOnClickListener(this@NotesFragment::onNoteAddClick)
}
activity.gainAttentionFAB()
val adapter = NoteListAdapter( val adapter = NoteListAdapter(
activity = activity, activity = activity,
onNoteClick = this::onNoteClick, onNoteClick = this::onNoteClick,
@ -153,4 +86,38 @@ class NotesFragment : Fragment(), CoroutineScope {
adapter.getSearchField()?.applyTo(adapter) adapter.getSearchField()?.applyTo(adapter)
} }
} }
private fun onNoteClick(note: Note) = launch {
val owner = withContext(Dispatchers.IO) {
manager.getOwner(note)
} as? Noteable
NoteDetailsDialog(
activity = activity,
owner = owner,
note = note,
).show()
}
private fun onNoteEditClick(note: Note) = launch {
val owner = withContext(Dispatchers.IO) {
manager.getOwner(note)
} as? Noteable
NoteEditorDialog(
activity = activity,
owner = owner,
editingNote = note,
profileId = App.profileId,
).show()
}
override suspend fun onFabClick() {
NoteEditorDialog(
activity = activity,
owner = null,
editingNote = null,
profileId = App.profileId,
).show()
}
} }

View File

@ -6,64 +6,45 @@ package pl.szczodrzynski.edziennik.ui.notifications
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.os.AsyncTask
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.NotificationsListFragmentBinding import pl.szczodrzynski.edziennik.databinding.NotificationsListFragmentBinding
import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty
import pl.szczodrzynski.edziennik.ext.startCoroutineTimer import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
import timber.log.Timber import timber.log.Timber
import kotlin.coroutines.CoroutineContext
class NotificationsListFragment : Fragment(), CoroutineScope { class NotificationsListFragment : BaseFragment<NotificationsListFragmentBinding, MainActivity>(
companion object { inflater = NotificationsListFragmentBinding::inflate,
private const val TAG = "NotificationsListFragment" ) {
}
private lateinit var app: App override fun getBottomSheetItems() = listOf(
private lateinit var activity: MainActivity BottomSheetPrimaryItem(true)
private lateinit var b: NotificationsListFragmentBinding .withTitle(R.string.menu_remove_notifications)
.withIcon(CommunityMaterial.Icon.cmd_delete_sweep_outline)
private val job: Job = Job() .withOnClickListener {
override val coroutineContext: CoroutineContext activity.bottomSheet.close()
get() = job + Dispatchers.Main launch(Dispatchers.IO) {
app.db.notificationDao().clearAll()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { }
activity = (getActivity() as MainActivity?) ?: return null Toast.makeText(
context ?: return null activity,
app = activity.application as App R.string.menu_remove_notifications_success,
b = NotificationsListFragmentBinding.inflate(inflater) Toast.LENGTH_SHORT
return b.root ).show()
} }
)
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()
}))
override suspend fun onViewReady(savedInstanceState: Bundle?) {
val adapter = NotificationsAdapter(activity) { notification -> val adapter = NotificationsAdapter(activity) { notification ->
val intent = Intent("android.intent.action.MAIN") val intent = Intent("android.intent.action.MAIN")
notification.fillIntent(intent) notification.fillIntent(intent)
@ -100,5 +81,5 @@ class NotificationsListFragment : Fragment(), CoroutineScope {
b.noData.isVisible = false b.noData.isVisible = false
} }
}) })
}} }
} }

View File

@ -1,39 +1,14 @@
package pl.szczodrzynski.edziennik.ui.settings package pl.szczodrzynski.edziennik.ui.settings
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.databinding.FragmentProfileManagerBinding import pl.szczodrzynski.edziennik.databinding.FragmentProfileManagerBinding
import pl.szczodrzynski.edziennik.utils.Themes import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
class ProfileManagerFragment : Fragment() {
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: FragmentProfileManagerBinding
/*
private val navController: NavController by lazy { Navigation.findNavController(b.root) }
*/
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null
if (context == null)
return null
app = activity.application as App
// activity, context and profile is valid
b = FragmentProfileManagerBinding.inflate(inflater)
b.refreshLayout.setParent(activity.swipeRefreshLayout)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (app.profile == null || !isAdded)
return
class ProfileManagerFragment : BaseFragment<FragmentProfileManagerBinding, MainActivity>(
inflater = FragmentProfileManagerBinding::inflate,
) {
override suspend fun onViewReady(savedInstanceState: Bundle?) {
} }
} }

Some files were not shown because too many files have changed in this diff Show More