[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 language="kotlin">
<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="ENUM_CONSTANTS_WRAP" value="2" />
</codeStyleSettings>

View File

@ -126,7 +126,7 @@
/ ____ \ (__| |_| |\ V /| | |_| | __/\__ \
/_/ \_\___|\__|_| \_/ |_|\__|_|\___||___/
-->
<activity android:name=".ui.base.CrashActivity"
<activity android:name=".ui.main.CrashActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:process=":error_activity"
android:exported="false"
@ -163,8 +163,7 @@
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:theme="@style/Theme.MaterialComponents.Light.DarkActionBar" />
<activity android:name=".ui.base.BuildInvalidActivity" android:exported="false" />
<activity android:name=".ui.settings.contributors.ContributorsActivity" android:exported="false" />
<activity android:name=".ui.main.BuildInvalidActivity" 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.enums.LoginType
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.Utils
import timber.log.Timber

View File

@ -19,6 +19,7 @@ import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import androidx.core.view.isVisible
import androidx.navigation.NavOptions
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.danimahardhika.cafebar.CafeBar
import com.danimahardhika.cafebar.CafeBarTheme
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.UpdateStateEvent
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.NavTargetLocation
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.UpdateAvailableDialog
import pl.szczodrzynski.edziennik.ui.dialogs.sync.UpdateProgressDialog
import pl.szczodrzynski.edziennik.ui.error.ErrorDetailsDialog
import pl.szczodrzynski.edziennik.ui.error.ErrorSnackbar
import pl.szczodrzynski.edziennik.ui.dialogs.ErrorDetailsDialog
import pl.szczodrzynski.edziennik.ui.main.ErrorSnackbar
import pl.szczodrzynski.edziennik.ui.event.EventManualDialog
import pl.szczodrzynski.edziennik.ui.login.LoginActivity
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.items.DrawerPrimaryItem
import timber.log.Timber
import java.io.IOException
import kotlin.coroutines.CoroutineContext
import kotlin.math.roundToInt
@ -97,7 +97,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
val errorSnackbar: ErrorSnackbar by lazy { ErrorSnackbar(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
private var pausedNavigationData: PausedNavigationData? = null
@ -264,9 +264,8 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
drawer.setUnreadCounterList(unreadCounters)
}
b.swipeRefreshLayout.isEnabled = true
b.swipeRefreshLayout.setOnRefreshListener { launch { syncCurrentFeature() } }
b.swipeRefreshLayout.setColorSchemeResources(
swipeRefreshLayout.setOnRefreshListener { launch { syncCurrentFeature() } }
swipeRefreshLayout.setColorSchemeResources(
R.color.md_blue_500,
R.color.md_amber_500,
R.color.md_green_500
@ -939,6 +938,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
val arguments = args
?: navBackStack.firstOrNull { it.first == navTarget }?.second
?: Bundle()
swipeRefreshLayout.isEnabled = false
bottomSheet.close()
bottomSheet.removeAllContextual()
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.resolveColor
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 timber.log.Timber
import java.time.Instant

View File

@ -69,6 +69,14 @@ class FirebaseManager(val app: App) {
)
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 ->
val token = result.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.toApiError
import pl.szczodrzynski.edziennik.ext.toErrorCode
import pl.szczodrzynski.edziennik.ui.error.ErrorDetailsDialog
import pl.szczodrzynski.edziennik.ui.error.ErrorSnackbar
import pl.szczodrzynski.edziennik.ui.dialogs.ErrorDetailsDialog
import pl.szczodrzynski.edziennik.ui.main.ErrorSnackbar
import pl.szczodrzynski.edziennik.ui.login.LoginInfo
import pl.szczodrzynski.edziennik.utils.models.Date
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.settings.ProfileManagerFragment
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.template.TemplateFragment
import pl.szczodrzynski.edziennik.ui.timetable.TimetableFragment
import pl.szczodrzynski.edziennik.ui.webpush.WebPushFragment
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
@ -177,6 +179,15 @@ enum class NavTarget(
popTo = HOME,
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(
id = 200,
fragmentClass = null,
@ -243,6 +254,11 @@ enum class NavTarget(
id = 140,
fragmentClass = WebPushFragment::class.java,
nameRes = R.string.menu_web_push,
),
CONTRIBUTORS(
id = 150,
fragmentClass = ContributorsFragment::class.java,
nameRes = R.string.contributors,
);
companion object {

View File

@ -31,34 +31,33 @@ fun Bundle?.getIntOrNull(key: String): Int? {
}
@Suppress("UNCHECKED_CAST")
fun <T : Any> Bundle?.get(key: String): T? {
return this?.get(key) as? T?
operator fun Bundle.set(key: String, value: Any) = 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)
else -> throw IllegalArgumentException("Couldn't serialize $key = $value")
}
@Suppress("UNCHECKED_CAST")
fun Bundle(vararg properties: Pair<String, Any?>): Bundle {
return Bundle().apply {
for (property in properties) {
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)
}
}
fun Bundle.putExtras(vararg properties: Pair<String, Any?>): Bundle {
for (property in properties) {
val (key, value) = property
this[key] = value ?: continue
}
return this
}
fun Bundle(vararg properties: Pair<String, Any?>) = Bundle().putExtras(*properties)
fun Intent(action: String? = null, vararg properties: Pair<String, Any?>): Intent {
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 Bundle.putExtras(vararg properties: Pair<String, Any?>) = putAll(Bundle(*properties))
fun Bundle.toJsonObject(): JsonObject {
val json = JsonObject()

View File

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

View File

@ -61,6 +61,12 @@ fun @receiver:AttrRes Int.resolveAttr(context: Context?): Int {
}
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
fun @receiver:AttrRes Int.resolveDimenAttr(context: Context): Float {
val typedValue = TypedValue()

View File

@ -13,7 +13,6 @@ import android.view.WindowManager
import android.widget.*
import androidx.annotation.StringRes
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.viewpager.widget.ViewPager
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) }
})
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() {
(parent as? ViewGroup)?.removeView(this)
}

View File

@ -8,135 +8,103 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.databinding.ViewDataBinding
import androidx.fragment.app.Fragment
import com.applandeo.materialcalendarview.EventDay
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import eu.szkolny.font.SzkolnyFont
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
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.EventType
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.enums.MetadataType
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaCalendarBinding
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.event.EventManualDialog
import pl.szczodrzynski.edziennik.utils.Themes
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
import kotlin.coroutines.CoroutineContext
class AgendaFragment : Fragment(), CoroutineScope {
class AgendaFragment : BaseFragment<ViewDataBinding, MainActivity>(
inflater = null,
) {
private lateinit var activity: MainActivity
private lateinit var b: ViewDataBinding
override fun inflate(
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 }
private var job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private var type: Int = Profile.AGENDA_DEFAULT
override fun getFab() = R.string.add to CommunityMaterial.Icon3.cmd_plus
override fun getMarkAsReadType() = MetadataType.EVENT
override fun getBottomSheetItems() = listOf(
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_add_event)
.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 (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
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
if (getActivity() == null || context == null) return null
activity = getActivity() as MainActivity
type = app.profile.config.ui.agendaViewType
b = when (type) {
Profile.AGENDA_DEFAULT -> FragmentAgendaDefaultBinding.inflate(inflater, container, false)
Profile.AGENDA_CALENDAR -> FragmentAgendaCalendarBinding.inflate(inflater, container, false)
else -> return null
override suspend fun onViewReady(savedInstanceState: Bundle?) {
when (app.profile.config.ui.agendaViewType) {
Profile.AGENDA_DEFAULT -> createDefaultAgendaView(
b as? FragmentAgendaDefaultBinding ?: return
)
Profile.AGENDA_CALENDAR -> createCalendarAgendaView(
b as? FragmentAgendaCalendarBinding ?: return
)
}
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 {
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()
}
override suspend fun onFabClick() {
EventManualDialog(
activity,
app.profileId,
defaultDate = AgendaFragmentDefault.selectedDate
).show()
}
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)
return@launch
return
checkEventTypes()
delay(500)
agendaDefault = AgendaFragmentDefault(activity, app, b)
agendaDefault?.initView(this@AgendaFragment)
}}}
}
private fun createCalendarAgendaView() { (b as? FragmentAgendaCalendarBinding)?.let { b -> launch {
private suspend fun createCalendarAgendaView(b: FragmentAgendaCalendarBinding) {
checkEventTypes()
delay(300)
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>()
events.forEach { event ->
@ -189,5 +159,5 @@ class AgendaFragment : Fragment(), CoroutineScope {
}}
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.TeacherAbsenceEvent
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.EventListAdapter
import pl.szczodrzynski.edziennik.ui.event.EventManualDialog

View File

@ -7,7 +7,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import pl.szczodrzynski.edziennik.R
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.utils.models.Date

View File

@ -7,7 +7,7 @@ import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.LinearLayoutManager
import pl.szczodrzynski.edziennik.R
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
class TeacherAbsenceDialog(

View File

@ -51,7 +51,6 @@ public class AnnouncementsFragment extends Fragment {
app = (App) activity.getApplication();
// activity, context and profile is valid
b = DataBindingUtil.inflate(inflater, R.layout.fragment_announcements, container, false);
b.refreshLayout.setParent(activity.getSwipeRefreshLayout());
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());
//RecyclerViewExpandableItemManager expMgr = new RecyclerViewExpandableItemManager(null);
@ -90,18 +85,6 @@ public class AnnouncementsFragment extends Fragment {
recyclerView.setLayoutManager(linearLayoutManager);
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 -> {
if (app == null || activity == null || b == null || !isAdded())
return;

View File

@ -13,7 +13,7 @@ import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
import pl.szczodrzynski.edziennik.databinding.AttendanceDetailsDialogBinding
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.utils.BetterLink
import pl.szczodrzynski.edziennik.core.manager.NoteManager

View File

@ -4,118 +4,59 @@
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 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.R
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.addOnPageSelectedListener
import pl.szczodrzynski.edziennik.ui.base.lazypager.FragmentLazyPagerAdapter
import pl.szczodrzynski.edziennik.ui.base.fragment.PagerFragment
import pl.szczodrzynski.edziennik.ui.dialogs.settings.AttendanceConfigDialog
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 {
private const val TAG = "AttendanceFragment"
const val VIEW_SUMMARY = 0
const val VIEW_DAYS = 1
const val VIEW_MONTHS = 2
const val VIEW_TYPES = 3
const val VIEW_LIST = 4
var pageSelection = 1
}
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: AttendanceFragmentBinding
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 var savedPageSelection
get() = app.profile.config.attendance.attendancePageSelection
set(value) {
app.profile.config.attendance.attendancePageSelection = value
}
}
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
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers
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.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.AttendanceMonth
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.utils.models.Date
import kotlin.coroutines.CoroutineContext
class AttendanceListFragment : LazyFragment(), CoroutineScope {
companion object {
private const val TAG = "AttendanceListFragment"
}
class AttendanceListFragment : BaseFragment<AttendanceListFragmentBinding, MainActivity>(
inflater = AttendanceListFragmentBinding::inflate,
) {
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: AttendanceListFragmentBinding
override fun getRefreshScrollingView() = b.list
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 expandSubjectId = 0L
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null
context ?: return null
app = activity.application as App
b = AttendanceListFragmentBinding.inflate(inflater)
return b.root
}
override fun onPageCreated(): Boolean { startCoroutineTimer(100L) {
if (!isAdded) return@startCoroutineTimer
override suspend fun onViewReady(savedInstanceState: Bundle?) {
viewType = arguments?.getInt("viewType") ?: AttendanceFragment.VIEW_DAYS
expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L
@ -77,11 +55,9 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
b.list.apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(context)
addOnScrollListener(onScrollListener)
}
}
adapter.notifyDataSetChanged()
setSwipeToRefresh(adapter.items.isNullOrEmpty())
if (firstRun) {
expandSubject(adapter)
@ -90,7 +66,7 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
// show/hide relevant views
b.progressBar.isVisible = false
if (adapter.items.isNullOrEmpty()) {
if (adapter.items.isEmpty()) {
b.list.isVisible = false
b.noData.isVisible = true
} else {
@ -102,7 +78,7 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
adapter.onAttendanceClick = {
AttendanceDetailsDialog(activity, it).show()
}
}; return true}
}
private fun expandSubject(adapter: AttendanceAdapter) {
var expandSubjectModel: GradesSubject? = null

View File

@ -6,9 +6,6 @@ package pl.szczodrzynski.edziennik.ui.attendance
import android.graphics.Color
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.Animation
import android.view.animation.Transformation
@ -19,8 +16,12 @@ import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.coroutines.*
import pl.szczodrzynski.edziennik.*
import kotlinx.coroutines.Dispatchers
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.full.AttendanceFull
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.ui.attendance.AttendanceFragment.Companion.VIEW_SUMMARY
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.utils.models.Date
import java.text.DecimalFormat
import kotlin.coroutines.CoroutineContext
class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
class AttendanceSummaryFragment : BaseFragment<AttendanceSummaryFragmentBinding, MainActivity>(
inflater = AttendanceSummaryFragmentBinding::inflate,
) {
companion object {
private const val TAG = "AttendanceSummaryFragment"
private var periodSelection = 0
}
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: AttendanceSummaryFragmentBinding
override fun getRefreshScrollingView() = b.list
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 expandSubjectId = 0L
private var attendance = listOf<AttendanceFull>()
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 = AttendanceSummaryFragmentBinding.inflate(inflater)
return b.root
}
override fun onPageCreated(): Boolean { startCoroutineTimer(100L) {
if (!isAdded) return@startCoroutineTimer
override suspend fun onViewReady(savedInstanceState: Bundle?) {
expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L
val adapter = AttendanceAdapter(activity, VIEW_SUMMARY)
@ -91,7 +75,6 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
}
}
adapter.notifyDataSetChanged()
setSwipeToRefresh(adapter.items.isNullOrEmpty())
if (firstRun) {
expandSubject(adapter)
@ -100,7 +83,7 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
// show/hide relevant views
b.progressBar.isVisible = false
if (adapter.items.isNullOrEmpty()) {
if (adapter.items.isEmpty()) {
b.statsLayout.isVisible = false
b.list.isVisible = false
b.noData.isVisible = true
@ -144,7 +127,7 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
adapter.notifyDataSetChanged()
}
}
}; return true}
}
private fun expandSubject(adapter: AttendanceAdapter) {
var expandSubjectModel: GradesSubject? = null

View File

@ -2,7 +2,7 @@
* 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 androidx.appcompat.app.AlertDialog

View File

@ -2,7 +2,7 @@
* 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.View

View File

@ -2,7 +2,7 @@
* 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.viewbinding.ViewBinding

View File

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

View File

@ -2,7 +2,7 @@
* 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.os.Build
@ -19,7 +19,6 @@ import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent
import pl.szczodrzynski.edziennik.ext.get
import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import pl.szczodrzynski.edziennik.utils.Utils
@ -46,7 +45,7 @@ class AttachmentsView @JvmOverloads constructor(
val activity = context as? AppCompatActivity ?: 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 attachmentNames = arguments.getStringArray("attachmentNames") ?: return
val attachmentSizes = arguments.getLongArray("attachmentSizes")

View File

@ -2,7 +2,7 @@
* 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.ContextWrapper

View File

@ -2,7 +2,7 @@
* 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.util.AttributeSet

View File

@ -2,7 +2,7 @@
* 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.util.AttributeSet

View File

@ -2,7 +2,7 @@
* 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.ContextWrapper

View File

@ -2,7 +2,7 @@
* 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.util.AttributeSet

View File

@ -2,7 +2,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.util.AttributeSet

View File

@ -2,7 +2,7 @@
* 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.ContextWrapper

View File

@ -1,63 +1,32 @@
package pl.szczodrzynski.edziennik.ui.behaviour
import android.graphics.Color
import android.os.AsyncTask
import android.os.Bundle
import android.view.*
import android.widget.Toast
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.App.Companion.profileId
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
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.enums.MetadataType
import pl.szczodrzynski.edziennik.databinding.FragmentBehaviourBinding
import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
import pl.szczodrzynski.edziennik.utils.Themes.getPrimaryTextColor
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
import java.util.*
import java.util.Locale
class BehaviourFragment : Fragment() {
class BehaviourFragment : BaseFragment<FragmentBehaviourBinding, MainActivity>(
inflater = FragmentBehaviourBinding::inflate,
) {
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: FragmentBehaviourBinding
override fun getMarkAsReadType() = MetadataType.NOTICE
private var displayMode = MODE_YEAR
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?) {
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()
}
)
override suspend fun onViewReady(savedInstanceState: Bundle?) {
b.toggleGroup.check(when (displayMode) {
0 -> R.id.allYear
1 -> R.id.semester1
@ -81,18 +50,8 @@ class BehaviourFragment : Fragment() {
val linearLayoutManager = LinearLayoutManager(context)
b.noticesView.setHasFixedSize(true)
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>? ->
if (app == null || activity == null || b == null || !isAdded) return@observe
if (!isAdded) return@observe
if (notices == null) {
b.noticesView.visibility = View.GONE
b.noticesNoData.visibility = View.VISIBLE

View File

@ -13,7 +13,7 @@ import kotlinx.coroutines.*
import okhttp3.*
import pl.szczodrzynski.edziennik.R
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 kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

View File

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

View File

@ -4,73 +4,20 @@
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.fragment.app.Fragment
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.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
import pl.szczodrzynski.edziennik.databinding.BasePagerFragmentBinding
import pl.szczodrzynski.edziennik.ui.base.fragment.PagerFragment
class LabFragment : Fragment(), CoroutineScope {
companion object {
private const val TAG = "LabFragment"
var pageSelection = 0
}
class LabFragment : PagerFragment<BasePagerFragmentBinding, AppCompatActivity>(
inflater = BasePagerFragmentBinding::inflate,
), CoroutineScope {
private lateinit var app: App
private lateinit var activity: AppCompatActivity
private lateinit var b: TemplateFragmentBinding
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local/private variables go here
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as 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)
}
}
override fun getTabLayout() = b.tabLayout
override fun getViewPager() = b.viewPager
override suspend fun onCreatePages() = listOf(
LabPageFragment() to "click me",
LabProfileFragment() to "JSON",
LabPlaygroundFragment() to "Playground",
)
}

View File

@ -6,9 +6,6 @@ package pl.szczodrzynski.edziennik.ui.debug
import android.os.Bundle
import android.os.Process
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
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.SCREEN_HTTP
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.config.Config
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
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.databinding.LabFragmentBinding
import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.ui.base.lazypager.LazyFragment
import pl.szczodrzynski.edziennik.ext.asBoldSpannable
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.utils.TextInputDropDown
import pl.szczodrzynski.fslogin.decode
import kotlin.coroutines.CoroutineContext
import kotlin.system.exitProcess
class LabPageFragment : LazyFragment(), CoroutineScope {
companion object {
private const val TAG = "LabPageFragment"
}
class LabPageFragment : BaseFragment<LabFragmentBinding, AppCompatActivity>(
inflater = LabFragmentBinding::inflate,
) {
private lateinit var app: App
private lateinit var activity: AppCompatActivity
private lateinit var b: LabFragmentBinding
override fun getRefreshScrollingView() = b.scrollView
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 = LabFragmentBinding.inflate(inflater)
return b.root
}
override fun onPageCreated(): Boolean {
override suspend fun onViewReady(savedInstanceState: Bundle?) {
b.app = app
if (app.profile.id == 0) {
@ -222,7 +210,5 @@ class LabPageFragment : LazyFragment(), CoroutineScope {
}.concat("\n\n")
b.cookies.text = text
}
return true
}
}

View File

@ -5,51 +5,21 @@
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 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.ext.asColoredSpannable
import pl.szczodrzynski.edziennik.ext.concat
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 kotlin.coroutines.CoroutineContext
class LabPlaygroundFragment : LazyFragment(), CoroutineScope {
companion object {
private const val TAG = "LabPlaygroundFragment"
}
class LabPlaygroundFragment : BaseFragment<LabPlaygroundBinding, AppCompatActivity>(
inflater = LabPlaygroundBinding::inflate,
) {
private lateinit var app: App
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 fun getRefreshScrollingView() = b.root
override suspend fun onViewReady(savedInstanceState: Bundle?) {
Timber.d("textColorSecondary: ${android.R.attr.textColorSecondary.resolveAttr(activity)}")
b.spanTest1.text = listOf(
"Text:", "android:textColorSecondary spannable (activity)".asColoredSpannable(
@ -62,7 +32,5 @@ class LabPlaygroundFragment : LazyFragment(), CoroutineScope {
android.R.attr.textColorSecondary.resolveAttr(context)
)
).concat(" ")
return true
}
}

View File

@ -5,9 +5,6 @@
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.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
@ -16,48 +13,30 @@ import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.JsonParser
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.R
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.databinding.TemplateListPageFragmentBinding
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.utils.SimpleDividerItemDecoration
import kotlin.coroutines.CoroutineContext
class LabProfileFragment : LazyFragment(), CoroutineScope {
class LabProfileFragment : BaseFragment<TemplateListPageFragmentBinding, AppCompatActivity>(
inflater = TemplateListPageFragmentBinding::inflate,
) {
companion object {
private const val TAG = "LabProfileFragment"
}
private lateinit var app: App
private lateinit var activity: AppCompatActivity
private lateinit var b: TemplateListPageFragmentBinding
override fun getRefreshScrollingView() = b.list
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local/private variables go here
private lateinit var adapter: LabJsonAdapter
private val loginStore by lazy {
app.db.loginStoreDao().getByIdNow(app.profile.loginStoreId)
}
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 = TemplateListPageFragmentBinding.inflate(inflater)
return b.root
}
override fun onPageCreated(): Boolean { startCoroutineTimer(100L) {
override suspend fun onViewReady(savedInstanceState: Bundle?) {
adapter = LabJsonAdapter(activity, onJsonElementClick = { item ->
try {
var parent: Any = Unit
@ -117,7 +96,12 @@ class LabProfileFragment : LazyFragment(), CoroutineScope {
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 -> {
val field = parent::class.java.getDeclaredField(objName)
field.isAccessible = true
@ -165,15 +149,13 @@ class LabProfileFragment : LazyFragment(), CoroutineScope {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(context)
addItemDecoration(SimpleDividerItemDecoration(context))
addOnScrollListener(onScrollListener)
}
// show/hide relevant views
b.progressBar.isVisible = false
b.list.isVisible = true
b.noData.isVisible = false
}; return true }
}
private fun showJson() {
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.ext.resolveDrawable
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
class BellSyncDialog(

View File

@ -13,7 +13,7 @@ import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
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.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time

View File

@ -9,7 +9,7 @@ import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import pl.szczodrzynski.edziennik.R
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.html.BetterHtml

View File

@ -2,7 +2,7 @@
* 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 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.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog
import pl.szczodrzynski.edziennik.ui.base.dialog.BaseDialog
class ErrorDetailsDialog(
activity: AppCompatActivity,

View File

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

View File

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

View File

@ -13,7 +13,7 @@ import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.StyledTextDialogBinding
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.core.manager.TextStylingManager.HtmlMode.SIMPLE
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.databinding.DialogConfigAgendaBinding
import pl.szczodrzynski.edziennik.ext.onChange
import pl.szczodrzynski.edziennik.ui.dialogs.base.ConfigDialog
import pl.szczodrzynski.edziennik.ui.base.dialog.ConfigDialog
class AgendaConfigDialog(
activity: AppCompatActivity,

View File

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

View File

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

View File

@ -10,7 +10,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.widget.addTextChangedListener
import pl.szczodrzynski.edziennik.R
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
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.onClick
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_WEIGHTED
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 pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.MessagesConfigDialogBinding
import pl.szczodrzynski.edziennik.ui.dialogs.base.ConfigDialog
import pl.szczodrzynski.edziennik.ui.base.dialog.ConfigDialog
class MessagesConfigDialog(
activity: AppCompatActivity,

View File

@ -11,7 +11,7 @@ import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ext.resolveString
import pl.szczodrzynski.edziennik.data.enums.NavTarget
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(
activity: AppCompatActivity,

View File

@ -9,7 +9,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.enums.NotificationType
import pl.szczodrzynski.edziennik.ext.resolveString
import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog
import pl.szczodrzynski.edziennik.ui.base.dialog.BaseDialog
class NotificationFilterDialog(
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.onClick
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(
activity: MainActivity,

View File

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

View File

@ -7,7 +7,7 @@ package pl.szczodrzynski.edziennik.ui.dialogs.settings
import androidx.appcompat.app.AppCompatActivity
import pl.szczodrzynski.edziennik.R
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(
activity: AppCompatActivity,

View File

@ -9,7 +9,7 @@ import androidx.appcompat.app.AppCompatActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.TimetableConfigDialogBinding
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
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.databinding.DialogRegisterUnavailableBinding
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
class RegisterUnavailableDialog(

View File

@ -6,7 +6,7 @@ package pl.szczodrzynski.edziennik.ui.dialogs.sync
import androidx.appcompat.app.AppCompatActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog
import pl.szczodrzynski.edziennik.ui.base.dialog.BaseDialog
class ServerMessageDialog(
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.resolveString
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
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.ext.Intent
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.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.startCoroutineTimer
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
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.ext.*
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.timetable.TimetableFragment
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.setText
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.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.html.BetterHtml
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.Color
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.PopupMenu
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import coil.imageLoader
import coil.request.ImageRequest
import com.github.bassaer.chatmessageview.model.IChatUser
import com.github.bassaer.chatmessageview.model.Message
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.Subscribe
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.ext.crc16
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.openUrl
import timber.log.Timber
import java.util.*
import java.util.Calendar
import kotlin.collections.set
import kotlin.coroutines.CoroutineContext
class FeedbackFragment : Fragment(), CoroutineScope {
companion object {
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
class FeedbackFragment : BaseFragment<FragmentFeedbackBinding, AppCompatActivity>(
inflater = FragmentFeedbackBinding::inflate,
) {
private val chatView: ChatView by lazy { b.chatView }
private val api by lazy { SzkolnyApi(app) }
@ -58,18 +47,6 @@ class FeedbackFragment : Fragment(), CoroutineScope {
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)
fun onFeedbackMessageEvent(event: FeedbackMessageEvent) {
EventBus.getDefault().removeStickyEvent(event)
@ -143,10 +120,8 @@ class FeedbackFragment : Fragment(), CoroutineScope {
}
}
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?) {
EventBus.getDefault().removeStickyEvent(FeedbackMessageEvent::class.java)
b.faqText.setOnClickListener { openFaq() }
b.faqButton.setOnClickListener { openFaq() }
@ -163,46 +138,44 @@ class FeedbackFragment : Fragment(), CoroutineScope {
setMessageMarginBottom(5)
}
launch {
val messages = withContext(Dispatchers.Default) {
val messages = app.db.feedbackMessageDao().allNow
isDev = App.devMode && messages.any { it.deviceId != null }
messages
}
val messages = withContext(Dispatchers.IO) {
val messages = app.db.feedbackMessageDao().allNow
isDev = App.devMode && messages.any { it.deviceId != null }
messages
}
b.targetDeviceLayout.visibility = if (isDev) View.VISIBLE else View.GONE
b.targetDeviceDropDown.onClick {
launchDeviceSelection()
}
b.targetDeviceLayout.visibility = if (isDev) View.VISIBLE else View.GONE
b.targetDeviceDropDown.onClick {
launchDeviceSelection()
}
if (isDev) {
messages.firstOrNull { it.received && it.devId == null }?.let {
currentDeviceId = it.deviceId
if (isDev) {
messages.firstOrNull { it.received && it.devId == null }?.let {
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}")
}
// 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 {
send(b.textInput.text.toString())
}
chatView.setOnClickSendButtonListener(View.OnClickListener {
send(chatView.inputText)
})
b.sendButton.onClick {
send(b.textInput.text.toString())
}
chatView.setOnClickSendButtonListener {
send(chatView.inputText)
}
}
@ -275,14 +248,4 @@ class FeedbackFragment : Fragment(), CoroutineScope {
private fun openFaq() {
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.ext.onClick
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.notes.setupNotesButton
import pl.szczodrzynski.edziennik.utils.BetterLink

View File

@ -4,50 +4,51 @@
package pl.szczodrzynski.edziennik.ui.grades
import android.os.AsyncTask
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import kotlinx.coroutines.*
import pl.szczodrzynski.edziennik.*
import kotlinx.coroutines.Dispatchers
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.enums.MetadataType
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
import pl.szczodrzynski.edziennik.databinding.GradesListFragmentBinding
import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.data.enums.MetadataType
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.grades.models.GradesAverages
import pl.szczodrzynski.edziennik.ui.grades.models.GradesSemester
import pl.szczodrzynski.edziennik.ui.grades.models.GradesStats
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.BottomSheetSeparatorItem
import kotlin.coroutines.CoroutineContext
import kotlin.math.max
class GradesListFragment : Fragment(), CoroutineScope {
companion object {
private const val TAG = "GradesFragment"
}
class GradesListFragment : BaseFragment<GradesListFragmentBinding, MainActivity>(
inflater = GradesListFragmentBinding::inflate,
) {
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: GradesListFragmentBinding
override fun getMarkAsReadType() = MetadataType.GRADE
override fun getBottomSheetItems() = listOf(
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
get() = app.gradesManager
private val dontCountEnabled
@ -56,18 +57,7 @@ class GradesListFragment : Fragment(), CoroutineScope {
get() = manager.dontCountGrades
private var expandSubjectId = 0L
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null
context ?: return null
app = activity.application as App
b = GradesListFragmentBinding.inflate(inflater)
b.refreshLayout.setParent(activity.swipeRefreshLayout)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { startCoroutineTimer(100L) {
if (!isAdded) return@startCoroutineTimer
override suspend fun onViewReady(savedInstanceState: Bundle?) {
expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L
val adapter = GradesAdapter(activity)
@ -92,7 +82,6 @@ class GradesListFragment : Fragment(), CoroutineScope {
b.list.apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(context)
addOnScrollListener(b.refreshLayout.onScrollListener)
}
}
adapter.notifyDataSetChanged()
@ -121,7 +110,7 @@ class GradesListFragment : Fragment(), CoroutineScope {
val otherSemester = subject.semesters.firstOrNull { it != semester }
var gradeSumOtherSemester = otherSemester?.averages?.normalWeightedSum
var gradeCountOtherSemester = otherSemester?.averages?.normalWeightedCount
if (gradeSumOtherSemester ?: 0f == 0f || gradeCountOtherSemester ?: 0f == 0f) {
if ((gradeSumOtherSemester ?: 0f) == 0f || (gradeCountOtherSemester ?: 0f) == 0f) {
gradeSumOtherSemester = otherSemester?.averages?.normalSum
gradeCountOtherSemester = otherSemester?.averages?.normalCount?.toFloat()
}
@ -137,27 +126,7 @@ class GradesListFragment : Fragment(), CoroutineScope {
"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) {
var expandSubjectModel: GradesSubject? = null

View File

@ -4,41 +4,34 @@ import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.os.Bundle
import android.text.InputType
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.PopupMenu
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.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.databinding.FragmentGradesEditorBinding
import pl.szczodrzynski.edziennik.ext.getFloat
import pl.szczodrzynski.edziennik.ext.getInt
import pl.szczodrzynski.edziennik.ext.getLong
import pl.szczodrzynski.edziennik.ext.input
import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
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 java.text.DecimalFormat
import java.util.*
import java.util.Locale
import kotlin.math.floor
class GradesEditorFragment : Fragment() {
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) }
*/
class GradesEditorFragment : BaseFragment<FragmentGradesEditorBinding, MainActivity>(
inflater = FragmentGradesEditorBinding::inflate,
) {
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 yearAverageBefore = 0.0f
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 = FragmentGradesEditorBinding.inflate(inflater)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (!isAdded)
return
override suspend fun onViewReady(savedInstanceState: Bundle?) {
subjectId = arguments.getLong("subjectId", -1)
semester = arguments.getInt("semester", 1)

View File

@ -5,16 +5,22 @@
package pl.szczodrzynski.edziennik.ui.home
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 com.google.android.material.card.MaterialCardView
import pl.szczodrzynski.edziennik.ui.home.HomeFragment.Companion.removeCard
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 {
private const val TAG = "CardItemTouchHelperCallback"
private const val DRAG_FLAGS = UP or DOWN
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)) {
dragCardView = viewHolder.itemView as MaterialCardView
dragCardView?.isDragged = true
refreshLayout?.isEnabled = false
onCanRefresh?.invoke(false)
}
else if (actionState == ACTION_STATE_IDLE && dragCardView != null) {
refreshLayout?.isEnabled = true
onCanRefresh?.invoke(true)
dragCardView?.isDragged = false
dragCardView = null
}

View File

@ -8,7 +8,7 @@ import androidx.appcompat.app.AppCompatActivity
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
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_GRADES
import pl.szczodrzynski.edziennik.ui.home.HomeCard.Companion.CARD_LUCKY_NUMBER

View File

@ -5,22 +5,19 @@
package pl.szczodrzynski.edziennik.ui.home
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.View.OnClickListener
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.view.AccessibilityDelegateCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
import androidx.core.widget.NestedScrollView
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial.Icon
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.BuildConfig
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.ext.hasUIFeature
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.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.BottomSheetSeparatorItem
import kotlin.coroutines.CoroutineContext
class HomeFragment : Fragment(), CoroutineScope {
class HomeFragment : BaseFragment<FragmentHomeBinding, MainActivity>(
inflater = FragmentHomeBinding::inflate,
) {
companion object {
private const val TAG = "HomeFragment"
fun swapCards(fromPosition: Int, toPosition: Int, cardAdapter: HomeCardAdapter): Boolean {
val fromCard = cardAdapter.items[fromPosition]
val toCard = cardAdapter.items[toPosition]
@ -75,75 +78,58 @@ class HomeFragment : Fragment(), CoroutineScope {
}
}
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: FragmentHomeBinding
override fun getBottomSheetItems() = listOf(
BottomSheetPrimaryItem(true)
.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
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) {
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 {
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()
if (cards.isEmpty()) {
cards += listOfNotNull(
@ -184,7 +170,9 @@ class HomeFragment : Fragment(), CoroutineScope {
}
val adapter = HomeCardAdapter(items)
val itemTouchHelper = ItemTouchHelper(CardItemTouchHelperCallback(adapter, b.refreshLayout))
val itemTouchHelper = ItemTouchHelper(CardItemTouchHelperCallback(adapter) {
canRefreshDisabled = !it
})
adapter.itemTouchHelper = itemTouchHelper
b.list.layoutManager = LinearLayoutManager(activity)
b.list.adapter = adapter

View File

@ -4,116 +4,49 @@
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 eu.szkolny.font.SzkolnyFont
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.R
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.databinding.HomeworkFragmentBinding
import pl.szczodrzynski.edziennik.databinding.BasePagerFragmentBinding
import pl.szczodrzynski.edziennik.ext.Bundle
import pl.szczodrzynski.edziennik.ext.addOnPageSelectedListener
import pl.szczodrzynski.edziennik.ui.base.lazypager.FragmentLazyPagerAdapter
import pl.szczodrzynski.edziennik.ui.base.fragment.PagerFragment
import pl.szczodrzynski.edziennik.ui.event.EventManualDialog
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
import kotlin.coroutines.CoroutineContext
class HomeworkFragment : Fragment(), CoroutineScope {
companion object {
private const val TAG = "HomeworkFragment"
var pageSelection = 0
}
class HomeworkFragment : PagerFragment<BasePagerFragmentBinding, MainActivity>(
inflater = BasePagerFragmentBinding::inflate,
), CoroutineScope {
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: HomeworkFragmentBinding
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local/private variables go here
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
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),
override fun getFab() = R.string.add to CommunityMaterial.Icon3.cmd_plus
override fun getMarkAsReadType() = MetadataType.HOMEWORK
override fun getBottomSheetItems() = listOf(
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.HOMEWORK, true) }
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 {
.withTitle(R.string.menu_add_event)
.withDescription(R.string.menu_add_event_desc)
.withIcon(SzkolnyFont.Icon.szf_calendar_plus_outline)
.withOnClickListener {
activity.bottomSheet.close()
EventManualDialog(activity, App.profileId, defaultType = Event.TYPE_HOMEWORK).show()
})
}
}
)
activity.gainAttention()
activity.gainAttentionFAB()
override fun getTabLayout() = b.tabLayout
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
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.databinding.HomeworkListFragmentBinding
import pl.szczodrzynski.edziennik.ext.getInt
import pl.szczodrzynski.edziennik.ext.startCoroutineTimer
import pl.szczodrzynski.edziennik.ui.base.lazypager.LazyFragment
import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
import pl.szczodrzynski.edziennik.ui.event.EventDetailsDialog
import pl.szczodrzynski.edziennik.ui.event.EventListAdapter
import pl.szczodrzynski.edziennik.ui.event.EventManualDialog
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import pl.szczodrzynski.edziennik.utils.models.Date
import kotlin.coroutines.CoroutineContext
class HomeworkListFragment : LazyFragment(), CoroutineScope {
companion object {
private const val TAG = "HomeworkListFragment"
}
class HomeworkListFragment : BaseFragment<HomeworkListFragmentBinding, MainActivity>(
inflater = HomeworkListFragmentBinding::inflate,
) {
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: HomeworkListFragmentBinding
override fun getRefreshScrollingView() = b.list
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 = HomeworkListFragmentBinding.inflate(inflater)
return b.root
}
override fun onPageCreated(): Boolean { startCoroutineTimer(100L) {
override suspend fun onViewReady(savedInstanceState: Bundle?) {
val homeworkDate = arguments.getInt("homeworkDate", HomeworkDate.CURRENT)
val today = Date.getToday()
@ -87,7 +63,6 @@ class HomeworkListFragment : LazyFragment(), CoroutineScope {
}
// show/hide relevant views
setSwipeToRefresh(events.isEmpty())
b.progressBar.isVisible = false
b.list.isVisible = events.isNotEmpty()
b.noData.isVisible = events.isEmpty()
@ -104,7 +79,6 @@ class HomeworkListFragment : LazyFragment(), CoroutineScope {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(context)
addItemDecoration(SimpleDividerItemDecoration(context))
addOnScrollListener(onScrollListener)
this.adapter = adapter
}
}
@ -112,5 +86,5 @@ class HomeworkListFragment : LazyFragment(), CoroutineScope {
// reapply the filter
adapter.getSearchField()?.applyTo(adapter)
})
}; return true }
}
}

View File

@ -9,6 +9,7 @@ import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.NavOptions
import androidx.navigation.Navigation
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
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.db.entity.LoginStore
import pl.szczodrzynski.edziennik.databinding.LoginActivityBinding
import pl.szczodrzynski.edziennik.ui.error.ErrorSnackbar
import pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoTouch
import pl.szczodrzynski.edziennik.ui.main.ErrorSnackbar
import kotlin.coroutines.CoroutineContext
class LoginActivity : AppCompatActivity(), CoroutineScope {
@ -34,7 +34,7 @@ class LoginActivity : AppCompatActivity(), CoroutineScope {
lateinit var navOptionsBuilder: NavOptions.Builder
val nav by lazy { Navigation.findNavController(this, R.id.nav_host_fragment) }
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()
override val coroutineContext: CoroutineContext

View File

@ -2,7 +2,7 @@
* 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.os.Bundle
@ -12,7 +12,6 @@ import com.mikepenz.iconics.utils.colorInt
import pl.szczodrzynski.edziennik.databinding.ActivityBuildInvalidBinding
import pl.szczodrzynski.edziennik.ext.app
import pl.szczodrzynski.edziennik.ext.onClick
import pl.szczodrzynski.edziennik.utils.Themes
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.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.ext.resolveAttr
import pl.szczodrzynski.edziennik.ext.resolveColor
import pl.szczodrzynski.edziennik.ext.resolveStyleAttr
import pl.szczodrzynski.edziennik.utils.html.BetterHtml
import kotlin.coroutines.CoroutineContext
@ -88,7 +89,7 @@ class CrashActivity : AppCompatActivity(), CoroutineScope {
val moreInfoButton = findViewById<Button>(R.id.crash_details_btn)
moreInfoButton.setOnClickListener {
MaterialAlertDialogBuilder(this, R.attr.materialAlertDialogMonospaceTheme.resolveAttr(this))
MaterialAlertDialogBuilder(this, R.attr.materialAlertDialogMonospaceTheme.resolveStyleAttr(this))
.setTitle(R.string.crash_details)
.setMessage(BetterHtml.fromHtml(context = null, getErrorString(intent, false)))
.setPositiveButton(R.string.close, null)

View File

@ -2,7 +2,7 @@
* 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 androidx.appcompat.app.AppCompatActivity
@ -14,6 +14,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.ui.dialogs.ErrorDetailsDialog
import pl.szczodrzynski.navlib.getColorFromAttr
import kotlin.coroutines.CoroutineContext

View File

@ -2,7 +2,7 @@
* 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 androidx.appcompat.app.AppCompatActivity

View File

@ -6,22 +6,27 @@ package pl.szczodrzynski.edziennik.ui.messages.compose
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AutoCompleteTextView
import android.widget.ScrollView
import android.widget.Toast
import androidx.core.view.isVisible
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
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.Subscribe
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.edziennik.EdziennikTask
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.Teacher
import pl.szczodrzynski.edziennik.data.enums.LoginType
import pl.szczodrzynski.edziennik.data.enums.NavTarget
import pl.szczodrzynski.edziennik.databinding.MessagesComposeFragmentBinding
import pl.szczodrzynski.edziennik.ext.Bundle
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.messages.list.MessagesFragment
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.BottomSheetSeparatorItem
import kotlin.coroutines.CoroutineContext
class MessagesComposeFragment : Fragment(), CoroutineScope {
class MessagesComposeFragment : BaseFragment<MessagesComposeFragmentBinding, MainActivity>(
inflater = MessagesComposeFragmentBinding::inflate,
) {
companion object {
private const val TAG = "MessagesComposeFragment"
}
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: MessagesComposeFragmentBinding
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
override fun getFab() = R.string.messages_compose_send to CommunityMaterial.Icon3.cmd_send_outline
override fun getBottomSheetItems() = listOf(
BottomSheetPrimaryItem(true)
.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()
}
)
private val manager
get() = app.messageManager
@ -75,25 +96,7 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
private var discardDraftItem: BottomSheetPrimaryItem? = null
private var draftMessageId: Long? = 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
// 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)
override suspend fun onViewReady(savedInstanceState: Bundle?) {
b.breakpoints.visibility = if (App.devMode) View.VISIBLE else View.GONE
b.breakpoints.setOnClickListener {
b.breakpoints.isEnabled = true
@ -110,37 +113,8 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
discardDraftDialog()
}
activity.bottomSheet.prependItems(
BottomSheetPrimaryItem(true)
.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()
}
getRecipientList()
createView()
}
private fun getMessageBody(): String {
@ -150,18 +124,16 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
b.text.text?.toString() ?: ""
}
private fun getRecipientList() {
private suspend fun getRecipientList() {
if (app.data.messagesConfig.syncRecipientList && System.currentTimeMillis() - app.profile.lastReceiversSync > 1 * DAY * 1000) {
activity.snackbar("Pobieranie listy odbiorców...")
EdziennikTask.recipientListGet(App.profileId).enqueue(activity)
}
else {
launch {
val list = withContext(Dispatchers.Default) {
app.db.teacherDao().getAllNow(App.profileId).filter { it.loginId != null }
}
updateRecipientList(list)
val list = withContext(Dispatchers.IO) {
app.db.teacherDao().getAllNow(App.profileId).filter { it.loginId != null }
}
updateRecipientList(list)
}
}
@ -259,18 +231,10 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
changedBody = true
}
}
}
activity.navView.bottomBar.apply {
fabEnable = true
fabExtendedText = getString(R.string.messages_compose_send)
fabIcon = CommunityMaterial.Icon3.cmd_send_outline
setFabOnClickListener {
sendMessage()
}
}
activity.gainAttentionFAB()
override suspend fun onFabClick() {
sendMessage()
}
private fun onBeforeNavigate(): Boolean {
@ -437,15 +401,13 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
override fun onResume() {
super.onResume()
if (!isAdded || !this::activity.isInitialized)
if (!isAdded)
return
activity.onBeforeNavigate = this::onBeforeNavigate
}
override fun onPause() {
super.onPause()
if (!this::activity.isInitialized)
return
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
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 kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
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.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.navlib.bottomsheet.items.BottomSheetPrimaryItem
import kotlin.coroutines.CoroutineContext
class MessagesFragment : Fragment(), CoroutineScope {
class MessagesFragment : PagerFragment<BasePagerFragmentBinding, MainActivity>(
inflater = BasePagerFragmentBinding::inflate,
) {
companion object {
private const val TAG = "MessagesFragment"
var pageSelection = 0
}
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: MessagesFragmentBinding
override fun getFab() = R.string.compose to CommunityMaterial.Icon3.cmd_pencil_outline
override fun getBottomSheetItems() = listOf(
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 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 = MessagesFragmentBinding.inflate(inflater)
b.refreshLayout.setParent(activity.swipeRefreshLayout)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (!isAdded) return
override fun getTabLayout() = b.tabLayout
override fun getViewPager() = b.viewPager
override suspend fun onCreatePages() = listOf(
MessagesListFragment().apply {
arguments = Bundle("messageType" to Message.TYPE_RECEIVED)
} to getString(R.string.messages_tab_received),
MessagesListFragment().apply {
arguments = Bundle("messageType" to Message.TYPE_SENT)
} to getString(R.string.messages_tab_sent),
MessagesListFragment().apply {
arguments = Bundle("messageType" to Message.TYPE_DELETED)
} to getString(R.string.messages_tab_deleted),
MessagesListFragment().apply {
arguments = Bundle("messageType" to Message.TYPE_DRAFT)
} to getString(R.string.messages_tab_draft),
)
override suspend fun onViewReady(savedInstanceState: Bundle?) {
val messageId = arguments?.getLong("messageId", -1L) ?: -1L
if (messageId != -1L) {
val args = Bundle()
args.putLong("messageId", messageId)
arguments?.remove("messageId")
activity.navigate(navTarget = NavTarget.MESSAGE, args = args)
return
}
val args = arguments
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()
super.onViewReady(savedInstanceState)
}
private val onPageDestroy = { position: Int, outState: Bundle? ->
arguments?.putBundle("page$position", outState)
}
override fun onDestroy() {
super.onDestroy()
(b.viewPager.adapter as? FragmentLazyPagerAdapter)?.fragments?.forEach {
it.first.onDestroy()
}
override suspend fun onFabClick() {
activity.navigate(navTarget = NavTarget.MESSAGE_COMPOSE)
}
}

View File

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

View File

@ -5,28 +5,30 @@
package pl.szczodrzynski.edziennik.ui.messages.single
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import com.mikepenz.iconics.utils.sizeDp
import kotlinx.coroutines.*
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
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.events.MessageGetEvent
import pl.szczodrzynski.edziennik.data.enums.LoginType
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.databinding.MessageFragmentBinding
import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.data.enums.LoginType
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.messages.MessagesUtils
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.Time
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
import pl.szczodrzynski.navlib.colorAttr
import kotlin.coroutines.CoroutineContext
import kotlin.math.min
class MessageFragment : Fragment(), CoroutineScope {
companion object {
private const val TAG = "MessageFragment"
}
class MessageFragment : BaseFragment<MessageFragmentBinding, MainActivity>(
inflater = MessageFragmentBinding::inflate,
) {
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: MessageFragmentBinding
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
override fun getBottomSheetItems() = listOf(
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
get() = app.messageManager
private lateinit var message: MessageFull
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 = 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()
}
)
override suspend fun onViewReady(savedInstanceState: Bundle?) {
b.closeButton.onClick { activity.navigateUp() }
// click to expand subject and sender
@ -88,7 +69,6 @@ class MessageFragment : Fragment(), CoroutineScope {
b.senderContainer.onClick {
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 {
launch {
@ -129,14 +109,12 @@ class MessageFragment : Fragment(), CoroutineScope {
EdziennikTask.messageGet(App.profileId, message).enqueue(activity)
}
launch {
message = manager.getMessage(App.profileId, arguments) ?: run {
activity.navigateUp()
return@launch
}
b.subject.text = message.subject
checkMessage()
message = manager.getMessage(App.profileId, arguments) ?: run {
activity.navigateUp()
return
}
b.subject.text = message.subject
checkMessage()
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
@ -253,17 +231,4 @@ class MessageFragment : Fragment(), CoroutineScope {
}, 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.databinding.NoteDetailsDialogBinding
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
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.ext.isNotNullNorBlank
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.utils.TextInputDropDown
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.data.db.entity.Noteable
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
class NoteListDialog(

View File

@ -5,99 +5,32 @@
package pl.szczodrzynski.edziennik.ui.notes
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
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.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Note
import pl.szczodrzynski.edziennik.data.db.entity.Noteable
import pl.szczodrzynski.edziennik.databinding.NotesFragmentBinding
import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import kotlin.coroutines.CoroutineContext
class NotesFragment : Fragment(), CoroutineScope {
companion object {
private const val TAG = "NotesFragment"
}
class NotesFragment : BaseFragment<NotesFragmentBinding, MainActivity>(
inflater = NotesFragmentBinding::inflate,
) {
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: NotesFragmentBinding
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
override fun getFab() =
R.string.notes_action_add to CommunityMaterial.Icon3.cmd_text_box_plus_outline
private val manager
get() = app.noteManager
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 = 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()
override suspend fun onViewReady(savedInstanceState: Bundle?) {
val adapter = NoteListAdapter(
activity = activity,
onNoteClick = this::onNoteClick,
@ -153,4 +86,38 @@ class NotesFragment : Fragment(), CoroutineScope {
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.content.Intent
import android.os.AsyncTask
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.*
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.NotificationsListFragmentBinding
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.Utils
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
import timber.log.Timber
import kotlin.coroutines.CoroutineContext
class NotificationsListFragment : Fragment(), CoroutineScope {
companion object {
private const val TAG = "NotificationsListFragment"
}
class NotificationsListFragment : BaseFragment<NotificationsListFragmentBinding, MainActivity>(
inflater = NotificationsListFragmentBinding::inflate,
) {
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: NotificationsListFragmentBinding
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null
context ?: return null
app = activity.application as App
b = NotificationsListFragmentBinding.inflate(inflater)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { startCoroutineTimer(100L) {
if (!isAdded) return@startCoroutineTimer
activity.bottomSheet.prependItems(
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_remove_notifications)
.withIcon(CommunityMaterial.Icon.cmd_delete_sweep_outline)
.withOnClickListener(View.OnClickListener {
activity.bottomSheet.close()
AsyncTask.execute { app.db.notificationDao().clearAll() }
Toast.makeText(activity, R.string.menu_remove_notifications_success, Toast.LENGTH_SHORT).show()
}))
override fun getBottomSheetItems() = listOf(
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_remove_notifications)
.withIcon(CommunityMaterial.Icon.cmd_delete_sweep_outline)
.withOnClickListener {
activity.bottomSheet.close()
launch(Dispatchers.IO) {
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 intent = Intent("android.intent.action.MAIN")
notification.fillIntent(intent)
@ -100,5 +81,5 @@ class NotificationsListFragment : Fragment(), CoroutineScope {
b.noData.isVisible = false
}
})
}}
}
}

View File

@ -1,39 +1,14 @@
package pl.szczodrzynski.edziennik.ui.settings
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.databinding.FragmentProfileManagerBinding
import pl.szczodrzynski.edziennik.utils.Themes
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
import pl.szczodrzynski.edziennik.ui.base.fragment.BaseFragment
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