[API] Improve Lab fragment. Fix OkHttp crashing on API <21.

This commit is contained in:
Kuba Szczodrzyński 2020-05-12 13:41:40 +02:00
parent fada483d55
commit 9261848369
17 changed files with 705 additions and 24 deletions

View File

@ -145,7 +145,11 @@ dependencies {
implementation("com.github.ozodrukh:CircularReveal:2.0.1@aar") {transitive = true} implementation("com.github.ozodrukh:CircularReveal:2.0.1@aar") {transitive = true}
implementation "com.heinrichreimersoftware:material-intro:1.5.8" // do not update implementation "com.heinrichreimersoftware:material-intro:1.5.8" // do not update
implementation "com.jaredrummler:colorpicker:1.0.2" 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.theartofdev.edmodo:android-image-cropper:2.8.0" // do not update
implementation "com.wdullaer:materialdatetimepicker:4.1.2" implementation "com.wdullaer:materialdatetimepicker:4.1.2"
implementation "com.yuyh.json:jsonviewer:1.0.6" implementation "com.yuyh.json:jsonviewer:1.0.6"

View File

@ -173,6 +173,8 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
} }
devMode = BuildConfig.DEBUG devMode = BuildConfig.DEBUG
if (BuildConfig.DEBUG)
debugMode = true
Signing.getCert(this) Signing.getCert(this)

View File

@ -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.models.ApiError
import pl.szczodrzynski.edziennik.data.api.task.IApiTask import pl.szczodrzynski.edziennik.data.api.task.IApiTask
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore 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.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull 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 { companion object {
private const val TAG = "EdziennikTask" private const val TAG = "EdziennikTask"
var profile: Profile? = null
var loginStore: LoginStore? = null
fun firstLogin(loginStore: LoginStore) = EdziennikTask(-1, FirstLoginRequest(loginStore)) fun firstLogin(loginStore: LoginStore) = EdziennikTask(-1, FirstLoginRequest(loginStore))
fun sync() = EdziennikTask(-1, SyncRequest()) fun sync() = EdziennikTask(-1, SyncRequest())
fun syncProfile(profileId: Int, viewIds: List<Pair<Int, Int>>? = null, onlyEndpoints: List<Int>? = null, arguments: JsonObject? = null) = EdziennikTask(profileId, SyncProfileRequest(viewIds, onlyEndpoints, arguments)) fun syncProfile(profileId: Int, viewIds: List<Pair<Int, Int>>? = null, onlyEndpoints: List<Int>? = 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 // save the profile ID and name as the current task's
taskName = app.getString(R.string.edziennik_notification_api_sync_title_format, profile.name) 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 private var edziennikInterface: EdziennikInterface? = null

View File

@ -26,7 +26,7 @@ class DumbCookieJar(
) : CookieJar { ) : CookieJar {
private val prefs = context.getSharedPreferences("cookies", Context.MODE_PRIVATE) private val prefs = context.getSharedPreferences("cookies", Context.MODE_PRIVATE)
private val sessionCookies = mutableSetOf<DumbCookie>() val sessionCookies = mutableSetOf<DumbCookie>()
private val savedCookies = mutableSetOf<DumbCookie>() private val savedCookies = mutableSetOf<DumbCookie>()
private fun save(dc: DumbCookie) { private fun save(dc: DumbCookie) {
sessionCookies.remove(dc) sessionCookies.remove(dc)

View File

@ -9,26 +9,25 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.sqlite.db.SimpleSQLiteQuery
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.data.db.entity.Event import pl.szczodrzynski.edziennik.addOnPageSelectedListener
import pl.szczodrzynski.edziennik.databinding.LabFragmentBinding import pl.szczodrzynski.edziennik.databinding.TemplateFragmentBinding
import pl.szczodrzynski.edziennik.onClick import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.FragmentLazyPagerAdapter
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class LabFragment : Fragment(), CoroutineScope { class LabFragment : Fragment(), CoroutineScope {
companion object { companion object {
private const val TAG = "LabFragment" private const val TAG = "LabFragment"
var pageSelection = 0
} }
private lateinit var app: App private lateinit var app: App
private lateinit var activity: MainActivity private lateinit var activity: MainActivity
private lateinit var b: LabFragmentBinding private lateinit var b: TemplateFragmentBinding
private val job: Job = Job() private val job: Job = Job()
override val coroutineContext: CoroutineContext override val coroutineContext: CoroutineContext
@ -40,29 +39,30 @@ class LabFragment : Fragment(), CoroutineScope {
activity = (getActivity() as MainActivity?) ?: return null activity = (getActivity() as MainActivity?) ?: return null
context ?: return null context ?: return null
app = activity.application as App app = activity.application as App
b = LabFragmentBinding.inflate(inflater) b = TemplateFragmentBinding.inflate(inflater)
b.refreshLayout.setParent(activity.swipeRefreshLayout)
return b.root return b.root
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (!isAdded) return if (!isAdded) return
b.last10unseen.onClick { val pagerAdapter = FragmentLazyPagerAdapter(
launch(Dispatchers.Default) { fragmentManager ?: return,
val events = app.db.eventDao().getAllNow(App.profileId) b.refreshLayout,
val ids = events.sortedBy { it.date }.filter { it.type == Event.TYPE_HOMEWORK }.takeLast(10) listOf(
ids.forEach { LabPageFragment() to "click me",
app.db.metadataDao().setSeen(App.profileId, it, false) LabProfileFragment() to "JSON"
} )
)
b.viewPager.apply {
offscreenPageLimit = 1
adapter = pagerAdapter
currentItem = pageSelection
addOnPageSelectedListener {
pageSelection = it
} }
} b.tabLayout.setupWithViewPager(this)
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}")
} }
} }
} }

View File

@ -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<RecyclerView.ViewHolder>(), 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<Any> {
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<Any>()
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<View>(R.id.dropdownIcon)?.let { dropdownIcon ->
ObjectAnimator.ofFloat(
dropdownIcon,
View.ROTATION,
if (model.state == STATE_CLOSED) 0f else 180f,
if (model.state == STATE_CLOSED) 180f else 0f
).setDuration(200).start();
}
// hide the preview, show summary
val preview = view?.findViewById<View>(R.id.previewContainer)
val summary = view?.findViewById<View>(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
}

View File

@ -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
}
}

View File

@ -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 }
}

View File

@ -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<JsonElement>(jsonArray.toMutableList())

View File

@ -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
)

View File

@ -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<JsonElement>(jsonObject.entrySet().map { it.value }.toMutableList())

View File

@ -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<LabJsonArray, LabJsonAdapter> {
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"
}
}

View File

@ -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<LabJsonElement, LabJsonAdapter> {
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("")
}
}

View File

@ -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<LabJsonObject, LabJsonAdapter> {
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"
}
}

View File

@ -54,6 +54,13 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Remove all homework body (null)" android:text="Remove all homework body (null)"
android:textAllCaps="false" /> android:textAllCaps="false" />
<TextView
android:id="@+id/cookies"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="monospace"
tools:text="Cookies:\n\nsynergia.librus.pl\n DZIENNIKSID=L01~1234567890abcdef"/>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
</layout> </layout>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2020-5-12.
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:orientation="horizontal"
android:padding="8dp">
<TextView
android:id="@+id/key"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:fontFamily="monospace"
android:maxLines="2"
android:ellipsize="end"
tools:text="lessonRanges: 8" />
<TextView
android:id="@+id/type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:text="Int"
android:textAppearance="@style/NavView.TextView.Helper" />
</LinearLayout>
</layout>

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2020-5-12.
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:orientation="vertical"
android:padding="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:gravity="center_vertical">
<TextView
android:id="@+id/key"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:fontFamily="monospace"
android:textAppearance="@style/NavView.TextView.Medium"
tools:text="lessonRanges" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
android:fontFamily="monospace"
android:text="JsonObject"
android:textAppearance="@style/NavView.TextView.Helper" />
<com.mikepenz.iconics.view.IconicsImageView
android:id="@+id/dropdownIcon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:scaleType="centerInside"
app:iiv_color="?android:textColorSecondary"
app:iiv_icon="cmd-chevron-down"
app:iiv_size="18dp"
tools:src="@android:drawable/ic_menu_more" />
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/previewContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:singleLine="true"
android:textAppearance="@style/NavView.TextView.Helper"
tools:text="{&quot;accountId&quot;:8765432,&quot;accountLogin&quot;:&quot;2345678u&quot;,&quot;accountToken&quot;:&quot;49azsnNZfxxfdSYb6lKU8NambrMXjeCrw7yA" />
<TextView
android:id="@+id/summaryContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/NavView.TextView.Helper"
tools:text="8 elements"
tools:visibility="gone" />
</FrameLayout>
</LinearLayout>
</layout>