From 9261848369387aa60b101adfdf8ecadee875fa86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Tue, 12 May 2020 13:41:40 +0200 Subject: [PATCH 1/2] [API] Improve Lab fragment. Fix OkHttp crashing on API <21. --- app/build.gradle | 6 +- .../java/pl/szczodrzynski/edziennik/App.kt | 2 + .../data/api/edziennik/EdziennikTask.kt | 6 + .../edziennik/network/cookie/DumbCookieJar.kt | 2 +- .../edziennik/ui/modules/debug/LabFragment.kt | 44 ++-- .../ui/modules/debug/LabJsonAdapter.kt | 191 ++++++++++++++++++ .../ui/modules/debug/LabPageFragment.kt | 92 +++++++++ .../ui/modules/debug/LabProfileFragment.kt | 82 ++++++++ .../ui/modules/debug/models/LabJsonArray.kt | 15 ++ .../ui/modules/debug/models/LabJsonElement.kt | 13 ++ .../ui/modules/debug/models/LabJsonObject.kt | 15 ++ .../debug/viewholder/JsonArrayViewHolder.kt | 46 +++++ .../debug/viewholder/JsonElementViewHolder.kt | 53 +++++ .../debug/viewholder/JsonObjectViewHolder.kt | 46 +++++ app/src/main/res/layout/lab_fragment.xml | 7 + app/src/main/res/layout/lab_item_element.xml | 34 ++++ app/src/main/res/layout/lab_item_object.xml | 75 +++++++ 17 files changed, 705 insertions(+), 24 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabJsonAdapter.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabPageFragment.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabProfileFragment.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/models/LabJsonArray.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/models/LabJsonElement.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/models/LabJsonObject.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/viewholder/JsonArrayViewHolder.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/viewholder/JsonElementViewHolder.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/viewholder/JsonObjectViewHolder.kt create mode 100644 app/src/main/res/layout/lab_item_element.xml create mode 100644 app/src/main/res/layout/lab_item_object.xml diff --git a/app/build.gradle b/app/build.gradle index 57b92b4a..9bc32c1a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -145,7 +145,11 @@ dependencies { implementation("com.github.ozodrukh:CircularReveal:2.0.1@aar") {transitive = true} implementation "com.heinrichreimersoftware:material-intro:1.5.8" // do not update implementation "com.jaredrummler:colorpicker:1.0.2" - implementation "com.squareup.okhttp3:okhttp:3.12.2" + implementation("com.squareup.okhttp3:okhttp") { + version { + strictly "3.12.2" + } + } implementation "com.theartofdev.edmodo:android-image-cropper:2.8.0" // do not update implementation "com.wdullaer:materialdatetimepicker:4.1.2" implementation "com.yuyh.json:jsonviewer:1.0.6" diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/App.kt b/app/src/main/java/pl/szczodrzynski/edziennik/App.kt index 3a9c0725..568538e7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/App.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/App.kt @@ -173,6 +173,8 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope { } devMode = BuildConfig.DEBUG + if (BuildConfig.DEBUG) + debugMode = true Signing.getCert(this) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/EdziennikTask.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/EdziennikTask.kt index 13de2f17..2b0498a7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/EdziennikTask.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/EdziennikTask.kt @@ -19,6 +19,7 @@ import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.task.IApiTask import pl.szczodrzynski.edziennik.data.db.entity.LoginStore +import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.EventFull @@ -28,6 +29,9 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa companion object { private const val TAG = "EdziennikTask" + var profile: Profile? = null + var loginStore: LoginStore? = null + fun firstLogin(loginStore: LoginStore) = EdziennikTask(-1, FirstLoginRequest(loginStore)) fun sync() = EdziennikTask(-1, SyncRequest()) fun syncProfile(profileId: Int, viewIds: List>? = null, onlyEndpoints: List? = null, arguments: JsonObject? = null) = EdziennikTask(profileId, SyncProfileRequest(viewIds, onlyEndpoints, arguments)) @@ -59,6 +63,8 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa // save the profile ID and name as the current task's taskName = app.getString(R.string.edziennik_notification_api_sync_title_format, profile.name) } + EdziennikTask.profile = this.profile + EdziennikTask.loginStore = this.loginStore } private var edziennikInterface: EdziennikInterface? = null diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/network/cookie/DumbCookieJar.kt b/app/src/main/java/pl/szczodrzynski/edziennik/network/cookie/DumbCookieJar.kt index ca11996f..39cec6eb 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/network/cookie/DumbCookieJar.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/network/cookie/DumbCookieJar.kt @@ -26,7 +26,7 @@ class DumbCookieJar( ) : CookieJar { private val prefs = context.getSharedPreferences("cookies", Context.MODE_PRIVATE) - private val sessionCookies = mutableSetOf() + val sessionCookies = mutableSetOf() private val savedCookies = mutableSetOf() private fun save(dc: DumbCookie) { sessionCookies.remove(dc) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabFragment.kt index 45472cf9..60c30cf9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabFragment.kt @@ -9,26 +9,25 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment -import androidx.sqlite.db.SimpleSQLiteQuery import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import kotlinx.coroutines.launch import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.MainActivity -import pl.szczodrzynski.edziennik.data.db.entity.Event -import pl.szczodrzynski.edziennik.databinding.LabFragmentBinding -import pl.szczodrzynski.edziennik.onClick +import pl.szczodrzynski.edziennik.addOnPageSelectedListener +import pl.szczodrzynski.edziennik.databinding.TemplateFragmentBinding +import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.FragmentLazyPagerAdapter import kotlin.coroutines.CoroutineContext class LabFragment : Fragment(), CoroutineScope { companion object { private const val TAG = "LabFragment" + var pageSelection = 0 } private lateinit var app: App private lateinit var activity: MainActivity - private lateinit var b: LabFragmentBinding + private lateinit var b: TemplateFragmentBinding private val job: Job = Job() override val coroutineContext: CoroutineContext @@ -40,29 +39,30 @@ class LabFragment : Fragment(), CoroutineScope { activity = (getActivity() as MainActivity?) ?: return null context ?: return null app = activity.application as App - b = LabFragmentBinding.inflate(inflater) + b = TemplateFragmentBinding.inflate(inflater) + b.refreshLayout.setParent(activity.swipeRefreshLayout) return b.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { if (!isAdded) return - b.last10unseen.onClick { - launch(Dispatchers.Default) { - val events = app.db.eventDao().getAllNow(App.profileId) - val ids = events.sortedBy { it.date }.filter { it.type == Event.TYPE_HOMEWORK }.takeLast(10) - ids.forEach { - app.db.metadataDao().setSeen(App.profileId, it, false) - } + val pagerAdapter = FragmentLazyPagerAdapter( + fragmentManager ?: return, + b.refreshLayout, + listOf( + LabPageFragment() to "click me", + LabProfileFragment() to "JSON" + ) + ) + b.viewPager.apply { + offscreenPageLimit = 1 + adapter = pagerAdapter + currentItem = pageSelection + addOnPageSelectedListener { + pageSelection = it } - } - - b.rodo.onClick { - app.db.teacherDao().query(SimpleSQLiteQuery("UPDATE teachers SET teacherSurname = \"\" WHERE profileId = ${App.profileId}")) - } - - b.removeHomework.onClick { - app.db.eventDao().getRawNow("UPDATE events SET homeworkBody = NULL WHERE profileId = ${App.profileId}") + b.tabLayout.setupWithViewPager(this) } } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabJsonAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabJsonAdapter.kt new file mode 100644 index 00000000..d1543cfc --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabJsonAdapter.kt @@ -0,0 +1,191 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-12. + */ + +package pl.szczodrzynski.edziennik.ui.modules.debug + +import android.animation.ObjectAnimator +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.isInvisible +import androidx.recyclerview.widget.RecyclerView +import com.google.gson.JsonArray +import com.google.gson.JsonElement +import com.google.gson.JsonObject +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.R +import pl.szczodrzynski.edziennik.ui.modules.debug.models.LabJsonArray +import pl.szczodrzynski.edziennik.ui.modules.debug.models.LabJsonElement +import pl.szczodrzynski.edziennik.ui.modules.debug.models.LabJsonObject +import pl.szczodrzynski.edziennik.ui.modules.debug.viewholder.JsonArrayViewHolder +import pl.szczodrzynski.edziennik.ui.modules.debug.viewholder.JsonElementViewHolder +import pl.szczodrzynski.edziennik.ui.modules.debug.viewholder.JsonObjectViewHolder +import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel +import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder +import kotlin.coroutines.CoroutineContext + +class LabJsonAdapter( + val activity: AppCompatActivity, + var onJsonElementClick: ((item: LabJsonElement) -> Unit)? = null +) : RecyclerView.Adapter(), CoroutineScope { + companion object { + private const val TAG = "AttendanceAdapter" + private const val ITEM_TYPE_OBJECT = 0 + private const val ITEM_TYPE_ARRAY = 1 + private const val ITEM_TYPE_ELEMENT = 2 + const val STATE_CLOSED = 0 + const val STATE_OPENED = 1 + + fun expand(item: Any, level: Int): MutableList { + val json = when (item) { + is LabJsonObject -> item.jsonObject + is LabJsonArray -> item.jsonArray + is JsonObject -> item + is JsonArray -> item + is JsonPrimitive -> item + else -> return mutableListOf() + } + + return when (json) { + is JsonObject -> json.entrySet().mapNotNull { wrap(it.key, it.value, level) } + is JsonArray -> json.mapIndexedNotNull { index, jsonElement -> wrap(index.toString(), jsonElement, level) } + else -> listOf(LabJsonElement("?", json, level)) + }.toMutableList() + } + fun wrap(key: String, item: JsonElement, level: Int = 0): Any? { + return when (item) { + is JsonObject -> LabJsonObject(key, item, level + 1) + is JsonArray -> LabJsonArray(key, item, level + 1) + is JsonElement -> LabJsonElement(key, item, level + 1) + else -> null + } + } + } + + private val app = activity.applicationContext as App + + private val job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + + var items = mutableListOf() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + return when (viewType) { + ITEM_TYPE_OBJECT -> JsonObjectViewHolder(inflater, parent) + ITEM_TYPE_ARRAY -> JsonArrayViewHolder(inflater, parent) + ITEM_TYPE_ELEMENT -> JsonElementViewHolder(inflater, parent) + else -> throw IllegalArgumentException("Incorrect viewType") + } + } + + override fun getItemViewType(position: Int): Int { + return when (items[position]) { + is LabJsonObject -> ITEM_TYPE_OBJECT + is LabJsonArray -> ITEM_TYPE_ARRAY + is LabJsonElement -> ITEM_TYPE_ELEMENT + else -> throw IllegalArgumentException("Incorrect viewType") + } + } + + private val onClickListener = View.OnClickListener { view -> + val model = view.getTag(R.string.tag_key_model) + if (model is LabJsonElement) { + onJsonElementClick?.invoke(model) + return@OnClickListener + } + if (model !is ExpandableItemModel<*>) + return@OnClickListener + expandModel(model, view) + } + + fun expandModel(model: ExpandableItemModel<*>?, view: View?, notifyAdapter: Boolean = true) { + model ?: return + val position = items.indexOf(model) + if (position == -1) + return + + view?.findViewById(R.id.dropdownIcon)?.let { dropdownIcon -> + ObjectAnimator.ofFloat( + dropdownIcon, + View.ROTATION, + if (model.state == STATE_CLOSED) 0f else 180f, + if (model.state == STATE_CLOSED) 180f else 0f + ).setDuration(200).start(); + } + + // hide the preview, show summary + val preview = view?.findViewById(R.id.previewContainer) + val summary = view?.findViewById(R.id.summaryContainer) + preview?.isInvisible = model.state == STATE_CLOSED + summary?.isInvisible = model.state != STATE_CLOSED + + if (model.state == STATE_CLOSED) { + + val subItems = when { + //model.items.isEmpty() -> listOf(AttendanceEmpty()) + else -> expand(model, model.level) + } + + model.state = STATE_OPENED + items.addAll(position + 1, subItems) + if (notifyAdapter) notifyItemRangeInserted(position + 1, subItems.size) + } + else { + val start = position + 1 + var end: Int = items.size + for (i in start until items.size) { + val model1 = items[i] + val level = (model1 as? ExpandableItemModel<*>)?.level ?: 3 + if (level <= model.level) { + end = i + break + } else { + if (model1 is ExpandableItemModel<*> && model1.state == STATE_OPENED) { + model1.state = STATE_CLOSED + } + } + } + + if (end != -1) { + items.subList(start, end).clear() + if (notifyAdapter) notifyItemRangeRemoved(start, end - start) + } + + model.state = STATE_CLOSED + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val item = items[position] + if (holder !is BindableViewHolder<*, *>) + return + + val viewType = when (holder) { + is JsonObjectViewHolder -> ITEM_TYPE_OBJECT + is JsonArrayViewHolder -> ITEM_TYPE_ARRAY + is JsonElementViewHolder -> ITEM_TYPE_ELEMENT + else -> throw IllegalArgumentException("Incorrect viewType") + } + holder.itemView.setTag(R.string.tag_key_view_type, viewType) + holder.itemView.setTag(R.string.tag_key_position, position) + holder.itemView.setTag(R.string.tag_key_model, item) + + when { + holder is JsonObjectViewHolder && item is LabJsonObject -> holder.onBind(activity, app, item, position, this) + holder is JsonArrayViewHolder && item is LabJsonArray -> holder.onBind(activity, app, item, position, this) + holder is JsonElementViewHolder && item is LabJsonElement -> holder.onBind(activity, app, item, position, this) + } + + holder.itemView.setOnClickListener(onClickListener) + } + + override fun getItemCount() = items.size +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabPageFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabPageFragment.kt new file mode 100644 index 00000000..bad9fd9f --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabPageFragment.kt @@ -0,0 +1,92 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-12. + */ + +package pl.szczodrzynski.edziennik.ui.modules.debug + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.sqlite.db.SimpleSQLiteQuery +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.db.entity.Event +import pl.szczodrzynski.edziennik.databinding.LabFragmentBinding +import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment +import pl.szczodrzynski.fslogin.decode +import kotlin.coroutines.CoroutineContext + +class LabPageFragment : LazyFragment(), CoroutineScope { + companion object { + private const val TAG = "LabPageFragment" + } + + private lateinit var app: App + private lateinit var activity: MainActivity + private lateinit var b: LabFragmentBinding + + 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 = LabFragmentBinding.inflate(inflater) + return b.root + } + + override fun onPageCreated(): Boolean { + b.last10unseen.onClick { + launch(Dispatchers.Default) { + val events = app.db.eventDao().getAllNow(App.profileId) + val ids = events.sortedBy { it.date }.filter { it.type == Event.TYPE_HOMEWORK }.takeLast(10) + ids.forEach { + app.db.metadataDao().setSeen(App.profileId, it, false) + } + } + } + + b.rodo.onClick { + app.db.teacherDao().query(SimpleSQLiteQuery("UPDATE teachers SET teacherSurname = \"\" WHERE profileId = ${App.profileId}")) + } + + b.removeHomework.onClick { + app.db.eventDao().getRawNow("UPDATE events SET homeworkBody = NULL WHERE profileId = ${App.profileId}") + } + + val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(activity) + startCoroutineTimer(500L, 300L) { + val text = app.cookieJar.sessionCookies + .map { it.cookie } + .sortedBy { it.domain() } + .groupBy { it.domain() } + .map { + listOf( + it.key.asBoldSpannable(), + ":\n", + it.value + .sortedBy { it.name() } + .map { + listOf( + " ", + it.name(), + "=", + it.value().decode().take(40).asItalicSpannable().asColoredSpannable(colorSecondary) + ).concat("") + }.concat("\n") + ).concat("") + }.concat("\n\n") + b.cookies.text = text + } + + return true + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabProfileFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabProfileFragment.kt new file mode 100644 index 00000000..cea59402 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabProfileFragment.kt @@ -0,0 +1,82 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-12. + */ + +package pl.szczodrzynski.edziennik.ui.modules.debug + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.recyclerview.widget.LinearLayoutManager +import com.google.gson.JsonObject +import com.google.gson.JsonParser +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.data.api.edziennik.EdziennikTask +import pl.szczodrzynski.edziennik.databinding.TemplateListPageFragmentBinding +import pl.szczodrzynski.edziennik.startCoroutineTimer +import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment +import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration +import kotlin.coroutines.CoroutineContext + +class LabProfileFragment : LazyFragment(), CoroutineScope { + companion object { + private const val TAG = "LabProfileFragment" + } + + private lateinit var app: App + private lateinit var activity: MainActivity + private lateinit var b: TemplateListPageFragmentBinding + + private val job: Job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + + // local/private variables go here + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + activity = (getActivity() as MainActivity?) ?: return null + context ?: return null + app = activity.application as App + b = TemplateListPageFragmentBinding.inflate(inflater) + return b.root + } + + override fun onPageCreated(): Boolean { startCoroutineTimer(100L) { + val adapter = LabJsonAdapter(activity) + val json = JsonObject().also { json -> + json.add("app.profile", app.profile.studentData) + json.add("app.config", JsonParser().parse(app.gson.toJson(app.config.values))) + EdziennikTask.profile?.let { + json.add("API.profile", it.studentData) + } ?: { + json.addProperty("API.profile", "null") + }() + EdziennikTask.loginStore?.let { + json.add("API.loginStore", it.data) + } ?: { + json.addProperty("API.loginStore", "null") + }() + } + adapter.items = LabJsonAdapter.expand(json, 0) + + b.list.adapter = adapter + b.list.apply { + 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 } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/models/LabJsonArray.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/models/LabJsonArray.kt new file mode 100644 index 00000000..a606e238 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/models/LabJsonArray.kt @@ -0,0 +1,15 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-12. + */ + +package pl.szczodrzynski.edziennik.ui.modules.debug.models + +import com.google.gson.JsonArray +import com.google.gson.JsonElement +import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel + +data class LabJsonArray( + val key: String, + val jsonArray: JsonArray, + override var level: Int +) : ExpandableItemModel(jsonArray.toMutableList()) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/models/LabJsonElement.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/models/LabJsonElement.kt new file mode 100644 index 00000000..e0e0e686 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/models/LabJsonElement.kt @@ -0,0 +1,13 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-12. + */ + +package pl.szczodrzynski.edziennik.ui.modules.debug.models + +import com.google.gson.JsonElement + +data class LabJsonElement( + val key: String, + val jsonElement: JsonElement, + var level: Int +) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/models/LabJsonObject.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/models/LabJsonObject.kt new file mode 100644 index 00000000..43a69201 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/models/LabJsonObject.kt @@ -0,0 +1,15 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-12. + */ + +package pl.szczodrzynski.edziennik.ui.modules.debug.models + +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel + +data class LabJsonObject( + val key: String, + val jsonObject: JsonObject, + override var level: Int +) : ExpandableItemModel(jsonObject.entrySet().map { it.value }.toMutableList()) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/viewholder/JsonArrayViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/viewholder/JsonArrayViewHolder.kt new file mode 100644 index 00000000..08d4cd3c --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/viewholder/JsonArrayViewHolder.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-12. + */ + +package pl.szczodrzynski.edziennik.ui.modules.debug.viewholder + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.view.ContextThemeWrapper +import androidx.core.view.isInvisible +import androidx.recyclerview.widget.RecyclerView +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.databinding.LabItemObjectBinding +import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter +import pl.szczodrzynski.edziennik.ui.modules.debug.LabJsonAdapter +import pl.szczodrzynski.edziennik.ui.modules.debug.models.LabJsonArray +import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder +import pl.szczodrzynski.edziennik.utils.Themes + +class JsonArrayViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + val b: LabItemObjectBinding = LabItemObjectBinding.inflate(inflater, parent, false) +) : RecyclerView.ViewHolder(b.root), BindableViewHolder { + companion object { + private const val TAG = "JsonArrayViewHolder" + } + + @SuppressLint("SetTextI18n") + override fun onBind(activity: AppCompatActivity, app: App, item: LabJsonArray, position: Int, adapter: LabJsonAdapter) { + val contextWrapper = ContextThemeWrapper(activity, Themes.appTheme) + + b.dropdownIcon.rotation = when (item.state) { + AttendanceAdapter.STATE_CLOSED -> 0f + else -> 180f + } + b.previewContainer.isInvisible = item.state != AttendanceAdapter.STATE_CLOSED + b.summaryContainer.isInvisible = item.state == AttendanceAdapter.STATE_CLOSED + + b.key.text = item.key + b.previewContainer.text = item.jsonArray.toString().take(200) + b.summaryContainer.text = item.jsonArray.size().toString() + " elements" + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/viewholder/JsonElementViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/viewholder/JsonElementViewHolder.kt new file mode 100644 index 00000000..8ce6b461 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/viewholder/JsonElementViewHolder.kt @@ -0,0 +1,53 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-12. + */ + +package pl.szczodrzynski.edziennik.ui.modules.debug.viewholder + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.view.ContextThemeWrapper +import androidx.recyclerview.widget.RecyclerView +import com.google.gson.JsonNull +import com.google.gson.JsonPrimitive +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.databinding.LabItemElementBinding +import pl.szczodrzynski.edziennik.ui.modules.debug.LabJsonAdapter +import pl.szczodrzynski.edziennik.ui.modules.debug.models.LabJsonElement +import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder +import pl.szczodrzynski.edziennik.utils.Themes + +class JsonElementViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + val b: LabItemElementBinding = LabItemElementBinding.inflate(inflater, parent, false) +) : RecyclerView.ViewHolder(b.root), BindableViewHolder { + companion object { + private const val TAG = "JsonObjectViewHolder" + } + + @SuppressLint("SetTextI18n") + override fun onBind(activity: AppCompatActivity, app: App, item: LabJsonElement, position: Int, adapter: LabJsonAdapter) { + val contextWrapper = ContextThemeWrapper(activity, Themes.appTheme) + + b.type.text = when (item.jsonElement) { + is JsonPrimitive -> when { + item.jsonElement.isNumber -> "Number" + item.jsonElement.isString -> "String" + item.jsonElement.isBoolean -> "Boolean" + else -> "Primitive" + } + is JsonNull -> "null" + else -> null + } + + val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(activity) + b.key.text = listOf( + item.key.asColoredSpannable(colorSecondary), + ": ", + item.jsonElement.toString().asItalicSpannable() + ).concat("") + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/viewholder/JsonObjectViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/viewholder/JsonObjectViewHolder.kt new file mode 100644 index 00000000..3131b0ea --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/viewholder/JsonObjectViewHolder.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-12. + */ + +package pl.szczodrzynski.edziennik.ui.modules.debug.viewholder + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.view.ContextThemeWrapper +import androidx.core.view.isInvisible +import androidx.recyclerview.widget.RecyclerView +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.databinding.LabItemObjectBinding +import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter +import pl.szczodrzynski.edziennik.ui.modules.debug.LabJsonAdapter +import pl.szczodrzynski.edziennik.ui.modules.debug.models.LabJsonObject +import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder +import pl.szczodrzynski.edziennik.utils.Themes + +class JsonObjectViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + val b: LabItemObjectBinding = LabItemObjectBinding.inflate(inflater, parent, false) +) : RecyclerView.ViewHolder(b.root), BindableViewHolder { + companion object { + private const val TAG = "JsonObjectViewHolder" + } + + @SuppressLint("SetTextI18n") + override fun onBind(activity: AppCompatActivity, app: App, item: LabJsonObject, position: Int, adapter: LabJsonAdapter) { + val contextWrapper = ContextThemeWrapper(activity, Themes.appTheme) + + b.dropdownIcon.rotation = when (item.state) { + AttendanceAdapter.STATE_CLOSED -> 0f + else -> 180f + } + b.previewContainer.isInvisible = item.state != AttendanceAdapter.STATE_CLOSED + b.summaryContainer.isInvisible = item.state == AttendanceAdapter.STATE_CLOSED + + b.key.text = item.key + b.previewContainer.text = item.jsonObject.toString().take(200) + b.summaryContainer.text = item.jsonObject.size().toString() + " elements" + } +} diff --git a/app/src/main/res/layout/lab_fragment.xml b/app/src/main/res/layout/lab_fragment.xml index e1cf029c..279c241c 100644 --- a/app/src/main/res/layout/lab_fragment.xml +++ b/app/src/main/res/layout/lab_fragment.xml @@ -54,6 +54,13 @@ android:layout_height="wrap_content" android:text="Remove all homework body (null)" android:textAllCaps="false" /> + + diff --git a/app/src/main/res/layout/lab_item_element.xml b/app/src/main/res/layout/lab_item_element.xml new file mode 100644 index 00000000..bdb58b9f --- /dev/null +++ b/app/src/main/res/layout/lab_item_element.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/lab_item_object.xml b/app/src/main/res/layout/lab_item_object.xml new file mode 100644 index 00000000..88fc19fa --- /dev/null +++ b/app/src/main/res/layout/lab_item_object.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + From 45c2948ed1818c5a97dcbd9fb12f553caaefc2e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Tue, 12 May 2020 18:23:29 +0200 Subject: [PATCH 2/2] [Login] Fix not showing errors after one successful login. --- .../edziennik/ui/modules/login/LoginProgressFragment.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.kt index 930ee367..7550b08c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.kt @@ -59,6 +59,8 @@ class LoginProgressFragment : Fragment(), CoroutineScope { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { if (!isAdded) return + EventBus.getDefault().removeStickyEvent(FirstLoginFinishedEvent::class.java) + val args = arguments ?: run { activity.error(ApiError(TAG, LOGIN_NO_ARGUMENTS)) nav.navigateUp() @@ -93,6 +95,7 @@ class LoginProgressFragment : Fragment(), CoroutineScope { @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) fun onFirstLoginFinishedEvent(event: FirstLoginFinishedEvent) { + EventBus.getDefault().removeStickyEvent(event) if (event.profileList.isEmpty()) { MaterialAlertDialogBuilder(activity) .setTitle(R.string.login_account_no_students)