From 77654365738ea300938c7b8d2f1284e422894994 Mon Sep 17 00:00:00 2001 From: jeancarlos Date: Sat, 23 Sep 2023 23:45:46 -0300 Subject: [PATCH 01/11] Refactoring TransactionPayloadAdapter to format json as JsonElements --- .../transaction/TransactionPayloadAdapter.kt | 178 +++++++++++++++++- .../transaction/TransactionPayloadFragment.kt | 72 ++++++- .../ui/transaction/TransactionViewModel.kt | 15 +- .../res/drawable/chucker_ic_collapse_all.xml | 5 + .../res/drawable/chuncker_ic_arrow_down.xml | 5 + .../chucker_transaction_item_body_json.xml | 99 ++++++++++ .../src/main/res/menu/chucker_transaction.xml | 8 + library/src/main/res/values/strings.xml | 2 + 8 files changed, 378 insertions(+), 6 deletions(-) create mode 100644 library/src/main/res/drawable/chucker_ic_collapse_all.xml create mode 100644 library/src/main/res/drawable/chuncker_ic_arrow_down.xml create mode 100644 library/src/main/res/layout/chucker_transaction_item_body_json.xml diff --git a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadAdapter.kt b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadAdapter.kt index e4483fd38..d4e2f0694 100644 --- a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadAdapter.kt +++ b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadAdapter.kt @@ -1,5 +1,6 @@ package com.chuckerteam.chucker.internal.ui.transaction +import android.animation.Animator import android.graphics.Bitmap import android.graphics.drawable.Drawable import android.text.SpannableStringBuilder @@ -10,6 +11,7 @@ import android.view.ViewGroup import androidx.core.text.getSpans import androidx.recyclerview.widget.RecyclerView import com.chuckerteam.chucker.R +import com.chuckerteam.chucker.databinding.ChuckerTransactionItemBodyJsonBinding import com.chuckerteam.chucker.databinding.ChuckerTransactionItemBodyLineBinding import com.chuckerteam.chucker.databinding.ChuckerTransactionItemHeadersBinding import com.chuckerteam.chucker.databinding.ChuckerTransactionItemImageBinding @@ -18,6 +20,9 @@ import com.chuckerteam.chucker.internal.support.SpanTextUtil import com.chuckerteam.chucker.internal.support.highlightWithDefinedColors import com.chuckerteam.chucker.internal.support.highlightWithDefinedColorsSubstring import com.chuckerteam.chucker.internal.support.indicesOf +import com.google.gson.JsonArray +import com.google.gson.JsonElement +import com.google.gson.JsonObject /** * Adapter responsible of showing the content of the Transaction Request/Response body. @@ -53,6 +58,12 @@ internal class TransactionBodyAdapter : RecyclerView.Adapter { + val bodyItemBinding = + ChuckerTransactionItemBodyJsonBinding.inflate(inflater, parent, false) + TransactionPayloadViewHolder.BodyJsonViewHolder(bodyItemBinding) + } + else -> { val imageItemBinding = ChuckerTransactionItemImageBinding.inflate(inflater, parent, false) TransactionPayloadViewHolder.ImageViewHolder(imageItemBinding) @@ -66,6 +77,7 @@ internal class TransactionBodyAdapter : RecyclerView.Adapter TYPE_HEADERS is TransactionPayloadItem.BodyLineItem -> TYPE_BODY_LINE + is TransactionPayloadItem.BodyJsonItem -> TYPE_BODY_JSON_LINE is TransactionPayloadItem.ImageItem -> TYPE_IMAGE } } @@ -144,7 +156,8 @@ internal class TransactionBodyAdapter : RecyclerView.Adapter { + bodyBinding.imgExpand.visibility = View.GONE + bodyBinding.rvSectionData.visibility = View.GONE + bodyBinding.txtStartValue.text = body.asString.plus(",") + } + body.isJsonObject -> body.asJsonObject.showObjects() + body.isJsonArray -> body.asJsonArray.showArrayObjects() + else -> Unit + } + } + + private fun JsonObject.showProperties() { + val attrList = mutableListOf() + + for ((key, value) in entrySet()) { + JsonObject().also { + it.add(key, value) + attrList.add(TransactionPayloadItem.BodyJsonItem(jsonElement = it)) + } + } + + bodyBinding.rvSectionData.visibility = View.VISIBLE + bodyBinding.rvSectionData.adapter = TransactionBodyAdapter().also { adapter -> + adapter.setItems(attrList) + } + } + + private fun JsonObject.showObjects() { + val obj = this + val keys = obj.keySet() + + if (keys.size == 0) return + + // { "key" : "value" } + if (keys.size == 1) { + val key = obj.keySet().first() + val value: JsonElement = obj.get(key) ?: return + val keyText = "\"" + key + "\"" + + bodyBinding.imgExpand.visibility = View.GONE + bodyBinding.txtKey.text = keyText + + when { + value.isJsonPrimitive -> { + val text = "\"" + value.asString + "\"," + bodyBinding.txtStartValue.text = text + bodyBinding.txtEndValue.visibility = View.GONE + } + + value.isJsonObject -> { + if (value.asJsonObject.isEmpty) { + bodyBinding.rvSectionData.visibility = View.GONE + bodyBinding.txtStartValue.text = "{}," + bodyBinding.txtEndValue.visibility = View.GONE + } else { + bodyBinding.root.setClickForValue(element = value) + } + } + + value.isJsonArray -> { + bodyBinding.root.setClickForValue(element = value) + } + } + } else { + // { "key1" : "value1", "key2" : "value2" } + bodyBinding.imgExpand.visibility = View.GONE + bodyBinding.txtKey.visibility = View.GONE + bodyBinding.txtDivider.visibility = View.GONE + bodyBinding.txtStartValue.visibility = View.GONE + bodyBinding.txtEndValue.visibility = View.GONE + obj.showProperties() + } + } + private fun JsonArray.showArrayObjects() { + map { + TransactionPayloadItem.BodyJsonItem(jsonElement = it.asJsonObject) + }.also { list -> + with(bodyBinding) { + imgExpand.visibility = View.GONE + txtKey.visibility = View.GONE + txtDivider.visibility = View.GONE + txtStartValue.visibility = View.GONE + txtEndValue.visibility = View.GONE + rvSectionData.visibility = View.VISIBLE + rvSectionData.adapter = TransactionBodyAdapter().also { adapter -> + adapter.setItems(list) + } + } + } + } + + private fun View.setClickForValue(element: JsonElement) = with(bodyBinding) { + var isOpen = false + + imgExpand.visibility = View.VISIBLE + txtStartValue.text = if (element.isJsonObject) "{...}" else "[...]" + txtEndValue.visibility = View.GONE + + setOnClickListener { view -> + isOpen = isOpen.not() + + imgExpand.animate() + .rotationBy(if (isOpen) OPEN_ROTATION_VALUE else CLOSE_ROTATION_VALUE) + .setListener(object : Animator.AnimatorListener { + override fun onAnimationStart(p0: Animator) { + view.isClickable = false + } + + override fun onAnimationEnd(p0: Animator) { + view.isClickable = true + + if (isOpen) { + rvSectionData.visibility = View.VISIBLE + txtStartValue.text = if (element.isJsonObject) "{" else "[" + txtEndValue.visibility = View.VISIBLE + txtEndValue.text = if (element.isJsonObject) "}," else "]," + } else { + rvSectionData.visibility = View.GONE + txtStartValue.text = + if (element.isJsonObject) "{...}," else "[...]," + txtEndValue.visibility = View.GONE + } + + rvSectionData.adapter = TransactionBodyAdapter().also { adapter -> + adapter.setItems( + listOf(TransactionPayloadItem.BodyJsonItem(jsonElement = element)) + ) + } + } + + @Suppress("EmptyFunctionBlock") + override fun onAnimationCancel(p0: Animator) { + } + + @Suppress("EmptyFunctionBlock") + override fun onAnimationRepeat(p0: Animator) { + } + }) + } + } + + internal companion object { + const val OPEN_ROTATION_VALUE = 180f + const val CLOSE_ROTATION_VALUE = -180f + } + } } internal sealed class TransactionPayloadItem { internal class HeaderItem(val headers: Spanned) : TransactionPayloadItem() internal class BodyLineItem(var line: SpannableStringBuilder) : TransactionPayloadItem() + internal class BodyJsonItem(val jsonElement: JsonElement?) : TransactionPayloadItem() internal class ImageItem(val image: Bitmap, val luminance: Double?) : TransactionPayloadItem() } diff --git a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt index d6ee45142..f79b40dec 100644 --- a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt +++ b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt @@ -34,6 +34,9 @@ import com.chuckerteam.chucker.internal.support.calculateLuminance import com.chuckerteam.chucker.internal.support.combineLatest import com.chuckerteam.chucker.internal.support.gone import com.chuckerteam.chucker.internal.support.visible +import com.google.gson.JsonParser +import com.google.gson.stream.JsonReader +import com.google.gson.stream.MalformedJsonException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -119,6 +122,8 @@ internal class TransactionPayloadFragment : payloadBinding.loadingProgress.visibility = View.VISIBLE val result = processPayload(payloadType, transaction, formatRequestBody) + .getCollapsableOrDefault() + if (result.isEmpty()) { showEmptyState() } else { @@ -142,7 +147,8 @@ internal class TransactionPayloadFragment : private fun onSearchScrollerButtonClick(goNext: Boolean) { // hide the keyboard if visible - val inputMethodManager = activity?.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager + val inputMethodManager = + activity?.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager if (inputMethodManager.isAcceptingText) { activity?.currentFocus?.clearFocus() inputMethodManager.hideSoftInputFromWindow(view?.windowToken, 0) @@ -199,6 +205,14 @@ internal class TransactionPayloadFragment : true } } + + menu.findItem(R.id.collapse).apply { + isVisible = true + setOnMenuItemClickListener { + viewModel.toggleCollapsableJson() + true + } + } } if (payloadType == PayloadType.REQUEST) { @@ -223,6 +237,7 @@ internal class TransactionPayloadFragment : PayloadType.REQUEST -> { (false == transaction?.isRequestBodyEncoded) && (0L != (transaction.requestPayloadSize)) } + PayloadType.RESPONSE -> { (false == transaction?.isResponseBodyEncoded) && (0L != (transaction.responsePayloadSize)) } @@ -256,7 +271,11 @@ internal class TransactionPayloadFragment : if (newText.isNotBlank() && newText.length > NUMBER_OF_IGNORED_SYMBOLS) { scrollableIndices.addAll( - payloadAdapter.highlightQueryWithColors(newText, backgroundSpanColor, foregroundSpanColor) + payloadAdapter.highlightQueryWithColors( + newText, + backgroundSpanColor, + foregroundSpanColor + ) ) } else { payloadAdapter.resetHighlight() @@ -393,7 +412,11 @@ internal class TransactionPayloadFragment : } } - private suspend fun saveToFile(type: PayloadType, uri: Uri, transaction: HttpTransaction): Boolean { + private suspend fun saveToFile( + type: PayloadType, + uri: Uri, + transaction: HttpTransaction + ): Boolean { return withContext(Dispatchers.IO) { try { requireContext().contentResolver.openFileDescriptor(uri, "w")?.use { @@ -456,4 +479,47 @@ internal class TransactionPayloadFragment : } return result } + + private fun MutableList.getCollapsableOrDefault(): List { + val default = this + + return if (viewModel.useJsonCollapsable) { + try { + mapToJsonElements() + } catch (t: MalformedJsonException) { + Toast.makeText(context, t.message, Toast.LENGTH_LONG).show() + Logger.error(t.message ?: "Error when formatting json") + viewModel.toggleCollapsableJson() + default + } + } else { + default + } + } + + private fun MutableList.mapToJsonElements(): List { + val bodyBuilder = StringBuilder() + val newList = arrayListOf() + + forEach { item -> + when (item) { + is TransactionPayloadItem.BodyLineItem -> bodyBuilder.append(item.line) + is TransactionPayloadItem.HeaderItem, + is TransactionPayloadItem.ImageItem -> newList.add(item) + + else -> Unit + } + } + + val reader = JsonReader(bodyBuilder.toString().reader()) + .also { it.isLenient = true } + + newList.add( + TransactionPayloadItem.BodyJsonItem( + jsonElement = JsonParser.parseReader(reader) + ) + ) + + return newList + } } diff --git a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt index 5f22a7a2f..de800c318 100644 --- a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt +++ b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt @@ -15,6 +15,10 @@ internal class TransactionViewModel(transactionId: Long) : ViewModel() { val encodeUrl: LiveData = mutableEncodeUrl + private var _useJsonCollapsable: Boolean = false + val useJsonCollapsable: Boolean + get() = _useJsonCollapsable + val transactionTitle: LiveData = RepositoryProvider.transaction() .getTransaction(transactionId) .combineLatest(encodeUrl) { transaction, encodeUrl -> @@ -34,10 +38,12 @@ internal class TransactionViewModel(transactionId: Long) : ViewModel() { val doesRequestBodyRequireEncoding: LiveData = RepositoryProvider.transaction() .getTransaction(transactionId) .map { transaction -> - transaction?.requestContentType?.contains("x-www-form-urlencoded", ignoreCase = true) ?: false + transaction?.requestContentType?.contains("x-www-form-urlencoded", ignoreCase = true) + ?: false } - val transaction: LiveData = RepositoryProvider.transaction().getTransaction(transactionId) + val transaction: LiveData = + RepositoryProvider.transaction().getTransaction(transactionId) val formatRequestBody: LiveData = doesRequestBodyRequireEncoding .combineLatest(encodeUrl) { requiresEncoding, encodeUrl -> @@ -49,6 +55,11 @@ internal class TransactionViewModel(transactionId: Long) : ViewModel() { fun encodeUrl(encode: Boolean) { mutableEncodeUrl.value = encode } + + fun toggleCollapsableJson() { + _useJsonCollapsable = !_useJsonCollapsable + mutableEncodeUrl.value = encodeUrl.value // Just to fire observer again + } } internal class TransactionViewModelFactory( diff --git a/library/src/main/res/drawable/chucker_ic_collapse_all.xml b/library/src/main/res/drawable/chucker_ic_collapse_all.xml new file mode 100644 index 000000000..484e38e41 --- /dev/null +++ b/library/src/main/res/drawable/chucker_ic_collapse_all.xml @@ -0,0 +1,5 @@ + + + diff --git a/library/src/main/res/drawable/chuncker_ic_arrow_down.xml b/library/src/main/res/drawable/chuncker_ic_arrow_down.xml new file mode 100644 index 000000000..45adee71e --- /dev/null +++ b/library/src/main/res/drawable/chuncker_ic_arrow_down.xml @@ -0,0 +1,5 @@ + + + diff --git a/library/src/main/res/layout/chucker_transaction_item_body_json.xml b/library/src/main/res/layout/chucker_transaction_item_body_json.xml new file mode 100644 index 000000000..b82112933 --- /dev/null +++ b/library/src/main/res/layout/chucker_transaction_item_body_json.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + diff --git a/library/src/main/res/menu/chucker_transaction.xml b/library/src/main/res/menu/chucker_transaction.xml index 422e5137c..e8a1aa289 100644 --- a/library/src/main/res/menu/chucker_transaction.xml +++ b/library/src/main/res/menu/chucker_transaction.xml @@ -43,4 +43,12 @@ android:visible="false" app:showAsAction="ifRoom"> + + + diff --git a/library/src/main/res/values/strings.xml b/library/src/main/res/values/strings.xml index ad7313078..c3a5c3498 100644 --- a/library/src/main/res/values/strings.xml +++ b/library/src/main/res/values/strings.xml @@ -32,6 +32,7 @@ Share as file Share as .har file Save body to file + Collapse all Failed to open file chooser File saved successfully! Failed to save file @@ -64,4 +65,5 @@ <Unable to discover GraphQL operation name> Buttons to scroll the search items Search Results: + : From 9e5d548c85b34f37d483acf2f816baad2e206c5e Mon Sep 17 00:00:00 2001 From: jeancarlos Date: Sun, 24 Sep 2023 13:40:33 -0300 Subject: [PATCH 02/11] Fixing try catch exception when formatting json --- .../internal/ui/transaction/TransactionPayloadFragment.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt index f79b40dec..2437b4ea4 100644 --- a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt +++ b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt @@ -35,8 +35,8 @@ import com.chuckerteam.chucker.internal.support.combineLatest import com.chuckerteam.chucker.internal.support.gone import com.chuckerteam.chucker.internal.support.visible import com.google.gson.JsonParser +import com.google.gson.JsonSyntaxException import com.google.gson.stream.JsonReader -import com.google.gson.stream.MalformedJsonException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -486,7 +486,7 @@ internal class TransactionPayloadFragment : return if (viewModel.useJsonCollapsable) { try { mapToJsonElements() - } catch (t: MalformedJsonException) { + } catch (t: JsonSyntaxException) { Toast.makeText(context, t.message, Toast.LENGTH_LONG).show() Logger.error(t.message ?: "Error when formatting json") viewModel.toggleCollapsableJson() From 4defa437815d7958f97374118d1f92e16cca61fd Mon Sep 17 00:00:00 2001 From: jean Date: Sun, 24 Sep 2023 14:18:05 -0300 Subject: [PATCH 03/11] Renaming references to collapsable suffix/prefix --- .../transaction/TransactionPayloadAdapter.kt | 40 +++++++++++-------- .../transaction/TransactionPayloadFragment.kt | 2 +- ...ker_transaction_item_body_collapsable.xml} | 1 - 3 files changed, 25 insertions(+), 18 deletions(-) rename library/src/main/res/layout/{chucker_transaction_item_body_json.xml => chucker_transaction_item_body_collapsable.xml} (99%) diff --git a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadAdapter.kt b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadAdapter.kt index d4e2f0694..134b63333 100644 --- a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadAdapter.kt +++ b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadAdapter.kt @@ -11,7 +11,7 @@ import android.view.ViewGroup import androidx.core.text.getSpans import androidx.recyclerview.widget.RecyclerView import com.chuckerteam.chucker.R -import com.chuckerteam.chucker.databinding.ChuckerTransactionItemBodyJsonBinding +import com.chuckerteam.chucker.databinding.ChuckerTransactionItemBodyCollapsableBinding import com.chuckerteam.chucker.databinding.ChuckerTransactionItemBodyLineBinding import com.chuckerteam.chucker.databinding.ChuckerTransactionItemHeadersBinding import com.chuckerteam.chucker.databinding.ChuckerTransactionItemImageBinding @@ -45,27 +45,33 @@ internal class TransactionBodyAdapter : RecyclerView.Adapter { - val headersItemBinding = ChuckerTransactionItemHeadersBinding.inflate(inflater, parent, false) + val headersItemBinding = + ChuckerTransactionItemHeadersBinding.inflate(inflater, parent, false) TransactionPayloadViewHolder.HeaderViewHolder(headersItemBinding) } TYPE_BODY_LINE -> { - val bodyItemBinding = ChuckerTransactionItemBodyLineBinding.inflate(inflater, parent, false) + val bodyItemBinding = + ChuckerTransactionItemBodyLineBinding.inflate(inflater, parent, false) TransactionPayloadViewHolder.BodyLineViewHolder(bodyItemBinding) } - TYPE_BODY_JSON_LINE -> { + TYPE_BODY_COLLAPSABLE -> { val bodyItemBinding = - ChuckerTransactionItemBodyJsonBinding.inflate(inflater, parent, false) + ChuckerTransactionItemBodyCollapsableBinding.inflate(inflater, parent, false) TransactionPayloadViewHolder.BodyJsonViewHolder(bodyItemBinding) } else -> { - val imageItemBinding = ChuckerTransactionItemImageBinding.inflate(inflater, parent, false) + val imageItemBinding = + ChuckerTransactionItemImageBinding.inflate(inflater, parent, false) TransactionPayloadViewHolder.ImageViewHolder(imageItemBinding) } } @@ -77,7 +83,7 @@ internal class TransactionBodyAdapter : RecyclerView.Adapter TYPE_HEADERS is TransactionPayloadItem.BodyLineItem -> TYPE_BODY_LINE - is TransactionPayloadItem.BodyJsonItem -> TYPE_BODY_JSON_LINE + is TransactionPayloadItem.BodyCollapsableItem -> TYPE_BODY_COLLAPSABLE is TransactionPayloadItem.ImageItem -> TYPE_IMAGE } } @@ -156,7 +162,7 @@ internal class TransactionBodyAdapter : RecyclerView.Adapter body.asJsonObject.showObjects() body.isJsonArray -> body.asJsonArray.showArrayObjects() else -> Unit @@ -267,12 +274,12 @@ internal sealed class TransactionPayloadViewHolder(view: View) : RecyclerView.Vi } private fun JsonObject.showProperties() { - val attrList = mutableListOf() + val attrList = mutableListOf() for ((key, value) in entrySet()) { JsonObject().also { it.add(key, value) - attrList.add(TransactionPayloadItem.BodyJsonItem(jsonElement = it)) + attrList.add(TransactionPayloadItem.BodyCollapsableItem(jsonElement = it)) } } @@ -328,9 +335,10 @@ internal sealed class TransactionPayloadViewHolder(view: View) : RecyclerView.Vi obj.showProperties() } } + private fun JsonArray.showArrayObjects() { map { - TransactionPayloadItem.BodyJsonItem(jsonElement = it.asJsonObject) + TransactionPayloadItem.BodyCollapsableItem(jsonElement = it.asJsonObject) }.also { list -> with(bodyBinding) { imgExpand.visibility = View.GONE @@ -380,7 +388,7 @@ internal sealed class TransactionPayloadViewHolder(view: View) : RecyclerView.Vi rvSectionData.adapter = TransactionBodyAdapter().also { adapter -> adapter.setItems( - listOf(TransactionPayloadItem.BodyJsonItem(jsonElement = element)) + listOf(TransactionPayloadItem.BodyCollapsableItem(jsonElement = element)) ) } } @@ -406,6 +414,6 @@ internal sealed class TransactionPayloadViewHolder(view: View) : RecyclerView.Vi internal sealed class TransactionPayloadItem { internal class HeaderItem(val headers: Spanned) : TransactionPayloadItem() internal class BodyLineItem(var line: SpannableStringBuilder) : TransactionPayloadItem() - internal class BodyJsonItem(val jsonElement: JsonElement?) : TransactionPayloadItem() + internal class BodyCollapsableItem(val jsonElement: JsonElement?) : TransactionPayloadItem() internal class ImageItem(val image: Bitmap, val luminance: Double?) : TransactionPayloadItem() } diff --git a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt index 2437b4ea4..03518e8ab 100644 --- a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt +++ b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt @@ -515,7 +515,7 @@ internal class TransactionPayloadFragment : .also { it.isLenient = true } newList.add( - TransactionPayloadItem.BodyJsonItem( + TransactionPayloadItem.BodyCollapsableItem( jsonElement = JsonParser.parseReader(reader) ) ) diff --git a/library/src/main/res/layout/chucker_transaction_item_body_json.xml b/library/src/main/res/layout/chucker_transaction_item_body_collapsable.xml similarity index 99% rename from library/src/main/res/layout/chucker_transaction_item_body_json.xml rename to library/src/main/res/layout/chucker_transaction_item_body_collapsable.xml index b82112933..5708aabf3 100644 --- a/library/src/main/res/layout/chucker_transaction_item_body_json.xml +++ b/library/src/main/res/layout/chucker_transaction_item_body_collapsable.xml @@ -37,7 +37,6 @@ android:textColor="@color/chucker_json_key_color" android:textDirection="ltr" android:textIsSelectable="true" - android:textStyle="bold" android:typeface="monospace" app:layout_constraintStart_toEndOf="@id/imgExpand" app:layout_constraintTop_toTopOf="@id/imgExpand" From 93fe1959d0dc6b81e577cf5a7c0f189fc4db037a Mon Sep 17 00:00:00 2001 From: jean Date: Sun, 24 Sep 2023 14:38:49 -0300 Subject: [PATCH 04/11] Handling collapsable menu icon and title. --- .../transaction/TransactionPayloadFragment.kt | 25 +++++++++++++++++-- .../ui/transaction/TransactionViewModel.kt | 2 +- .../res/drawable/chucker_ic_collapse_all.xml | 13 +++++++--- .../res/drawable/chucker_ic_expand_all.xml | 20 +++++++++++++++ .../src/main/res/menu/chucker_transaction.xml | 2 +- library/src/main/res/values/strings.xml | 1 + 6 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 library/src/main/res/drawable/chucker_ic_expand_all.xml diff --git a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt index 03518e8ab..3c2caffa2 100644 --- a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt +++ b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt @@ -12,6 +12,7 @@ import android.text.SpannableStringBuilder import android.view.LayoutInflater import android.view.Menu import android.view.MenuInflater +import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.view.inputmethod.InputMethodManager @@ -209,7 +210,7 @@ internal class TransactionPayloadFragment : menu.findItem(R.id.collapse).apply { isVisible = true setOnMenuItemClickListener { - viewModel.toggleCollapsableJson() + handleCollapseMenu() true } } @@ -243,6 +244,26 @@ internal class TransactionPayloadFragment : } } + private fun MenuItem.handleCollapseMenu() { + viewModel.toggleCollapsableJson() + + setTitle( + if (viewModel.isUsingCollapsableJson) { + R.string.chucker_expand_all + } else { + R.string.chucker_collapse_all + } + ) + + setIcon( + if (viewModel.isUsingCollapsableJson) { + R.drawable.chucker_ic_expand_all + } else { + R.drawable.chucker_ic_collapse_all + } + ) + } + override fun onAttach(context: Context) { super.onAttach(context) backgroundSpanColor = ContextCompat.getColor(context, R.color.chucker_background_span_color) @@ -483,7 +504,7 @@ internal class TransactionPayloadFragment : private fun MutableList.getCollapsableOrDefault(): List { val default = this - return if (viewModel.useJsonCollapsable) { + return if (viewModel.isUsingCollapsableJson) { try { mapToJsonElements() } catch (t: JsonSyntaxException) { diff --git a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt index de800c318..d54a24305 100644 --- a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt +++ b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt @@ -16,7 +16,7 @@ internal class TransactionViewModel(transactionId: Long) : ViewModel() { val encodeUrl: LiveData = mutableEncodeUrl private var _useJsonCollapsable: Boolean = false - val useJsonCollapsable: Boolean + val isUsingCollapsableJson: Boolean get() = _useJsonCollapsable val transactionTitle: LiveData = RepositoryProvider.transaction() diff --git a/library/src/main/res/drawable/chucker_ic_collapse_all.xml b/library/src/main/res/drawable/chucker_ic_collapse_all.xml index 484e38e41..ede475010 100644 --- a/library/src/main/res/drawable/chucker_ic_collapse_all.xml +++ b/library/src/main/res/drawable/chucker_ic_collapse_all.xml @@ -1,5 +1,10 @@ - - + + diff --git a/library/src/main/res/drawable/chucker_ic_expand_all.xml b/library/src/main/res/drawable/chucker_ic_expand_all.xml new file mode 100644 index 000000000..386de2ff1 --- /dev/null +++ b/library/src/main/res/drawable/chucker_ic_expand_all.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/library/src/main/res/menu/chucker_transaction.xml b/library/src/main/res/menu/chucker_transaction.xml index e8a1aa289..9d624bd63 100644 --- a/library/src/main/res/menu/chucker_transaction.xml +++ b/library/src/main/res/menu/chucker_transaction.xml @@ -49,6 +49,6 @@ android:title="@string/chucker_collapse_all" android:id="@+id/collapse" android:visible="false" - app:showAsAction="ifRoom"> + app:showAsAction="always"> diff --git a/library/src/main/res/values/strings.xml b/library/src/main/res/values/strings.xml index c3a5c3498..4643b6092 100644 --- a/library/src/main/res/values/strings.xml +++ b/library/src/main/res/values/strings.xml @@ -32,6 +32,7 @@ Share as file Share as .har file Save body to file + Expand all Collapse all Failed to open file chooser File saved successfully! From 4a9a3dcfdf5810125ead5b78d2d11baf927437c4 Mon Sep 17 00:00:00 2001 From: jean Date: Sun, 24 Sep 2023 14:48:35 -0300 Subject: [PATCH 05/11] Fixing null values for collapsable json. --- .../internal/ui/transaction/TransactionPayloadAdapter.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadAdapter.kt b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadAdapter.kt index 134b63333..a097d5653 100644 --- a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadAdapter.kt +++ b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadAdapter.kt @@ -306,8 +306,9 @@ internal sealed class TransactionPayloadViewHolder(view: View) : RecyclerView.Vi when { value.isJsonPrimitive -> { - val text = "\"" + value.asString + "\"," - bodyBinding.txtStartValue.text = text + val text = if (value.isJsonNull) "null" else "\"${value.asString}\"" + + bodyBinding.txtStartValue.text = text.plus(",") bodyBinding.txtEndValue.visibility = View.GONE } From c14da25e7630380460b236af2b6c50be87371dab Mon Sep 17 00:00:00 2001 From: jeancarlos Date: Mon, 2 Oct 2023 19:32:51 -0300 Subject: [PATCH 06/11] Changing collpse/expand icons dinamically --- .../transaction/TransactionPayloadFragment.kt | 57 ++++++++++++------- .../res/drawable/chucker_ic_expand_all.xml | 16 +----- .../src/main/res/menu/chucker_transaction.xml | 10 +++- 3 files changed, 50 insertions(+), 33 deletions(-) diff --git a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt index 3c2caffa2..3cb5bfbc7 100644 --- a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt +++ b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt @@ -206,14 +206,27 @@ internal class TransactionPayloadFragment : true } } + } - menu.findItem(R.id.collapse).apply { - isVisible = true - setOnMenuItemClickListener { - handleCollapseMenu() - true + if (shouldShowCollapsableIcon(transaction)) { + val collapseIcon = menu.findItem(R.id.collapse).also { item -> + item.setOnMenuItemClickListener { + it.handleCollapseMenu() + } + } + val expandIcon = menu.findItem(R.id.expand).also { item -> + item.setOnMenuItemClickListener { + it.handleCollapseMenu() } } + + if (viewModel.isUsingCollapsableJson) { + expandIcon.isVisible = true + collapseIcon.isVisible = false + } else { + collapseIcon.isVisible = true + expandIcon.isVisible = false + } } if (payloadType == PayloadType.REQUEST) { @@ -244,24 +257,30 @@ internal class TransactionPayloadFragment : } } - private fun MenuItem.handleCollapseMenu() { - viewModel.toggleCollapsableJson() + private fun shouldShowCollapsableIcon(transaction: HttpTransaction?): Boolean { + var isJsonContentType = false + var hasContent = false + val jsonContentType = "application/json" - setTitle( - if (viewModel.isUsingCollapsableJson) { - R.string.chucker_expand_all - } else { - R.string.chucker_collapse_all + when (payloadType) { + PayloadType.REQUEST -> { + isJsonContentType = transaction?.requestContentType?.contains(jsonContentType) == true + hasContent = (0L != (transaction?.requestPayloadSize)) } - ) - setIcon( - if (viewModel.isUsingCollapsableJson) { - R.drawable.chucker_ic_expand_all - } else { - R.drawable.chucker_ic_collapse_all + PayloadType.RESPONSE -> { + isJsonContentType = transaction?.responseContentType?.contains(jsonContentType) == true + hasContent = (0L != (transaction?.responsePayloadSize)) } - ) + } + + return isJsonContentType && hasContent + } + + private fun MenuItem.handleCollapseMenu(): Boolean { + viewModel.toggleCollapsableJson() + activity?.invalidateOptionsMenu() + return true } override fun onAttach(context: Context) { diff --git a/library/src/main/res/drawable/chucker_ic_expand_all.xml b/library/src/main/res/drawable/chucker_ic_expand_all.xml index 386de2ff1..e70dec124 100644 --- a/library/src/main/res/drawable/chucker_ic_expand_all.xml +++ b/library/src/main/res/drawable/chucker_ic_expand_all.xml @@ -2,19 +2,9 @@ android:width="24dp" android:height="24dp" android:tint="#F5F5F5" - android:viewportWidth="24" - android:viewportHeight="24"> + android:viewportWidth="960" + android:viewportHeight="960"> - - - - + android:pathData="M200,760v-240h80v160h160v80L200,760ZM680,440v-160L520,280v-80h240v240h-80Z" /> diff --git a/library/src/main/res/menu/chucker_transaction.xml b/library/src/main/res/menu/chucker_transaction.xml index 9d624bd63..13b501bdb 100644 --- a/library/src/main/res/menu/chucker_transaction.xml +++ b/library/src/main/res/menu/chucker_transaction.xml @@ -45,9 +45,17 @@ + + + From 82b82dc2e070f7f169e1fc08424df0af8d6b2f6c Mon Sep 17 00:00:00 2001 From: jeancarlos Date: Mon, 2 Oct 2023 20:22:28 -0300 Subject: [PATCH 07/11] Fixing null values for collapsable json. --- .../internal/ui/transaction/TransactionPayloadAdapter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadAdapter.kt b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadAdapter.kt index a097d5653..edabb01a0 100644 --- a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadAdapter.kt +++ b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadAdapter.kt @@ -305,7 +305,7 @@ internal sealed class TransactionPayloadViewHolder(view: View) : RecyclerView.Vi bodyBinding.txtKey.text = keyText when { - value.isJsonPrimitive -> { + value.isJsonPrimitive || value.isJsonNull -> { val text = if (value.isJsonNull) "null" else "\"${value.asString}\"" bodyBinding.txtStartValue.text = text.plus(",") From 1a0cac6c15c41f2422abe449369ba681730d5ec7 Mon Sep 17 00:00:00 2001 From: jeancarlos Date: Mon, 2 Oct 2023 20:26:06 -0300 Subject: [PATCH 08/11] Fixing arrow icon margin top --- .../res/layout/chucker_transaction_item_body_collapsable.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/library/src/main/res/layout/chucker_transaction_item_body_collapsable.xml b/library/src/main/res/layout/chucker_transaction_item_body_collapsable.xml index 5708aabf3..36ca4fb4a 100644 --- a/library/src/main/res/layout/chucker_transaction_item_body_collapsable.xml +++ b/library/src/main/res/layout/chucker_transaction_item_body_collapsable.xml @@ -34,6 +34,7 @@ android:layout_height="wrap_content" android:gravity="start" android:minLines="1" + android:layout_marginTop="4dp" android:textColor="@color/chucker_json_key_color" android:textDirection="ltr" android:textIsSelectable="true" From aedb2a1ccbadb876772332b33782840b3dea544d Mon Sep 17 00:00:00 2001 From: jeancarlos Date: Tue, 3 Oct 2023 13:18:24 -0300 Subject: [PATCH 09/11] Removing unnecessary change. --- .../chuckerteam/chucker/internal/support/NotificationHelper.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/library/src/main/kotlin/com/chuckerteam/chucker/internal/support/NotificationHelper.kt b/library/src/main/kotlin/com/chuckerteam/chucker/internal/support/NotificationHelper.kt index 9655dd2cc..499d1ce9f 100644 --- a/library/src/main/kotlin/com/chuckerteam/chucker/internal/support/NotificationHelper.kt +++ b/library/src/main/kotlin/com/chuckerteam/chucker/internal/support/NotificationHelper.kt @@ -13,6 +13,7 @@ import com.chuckerteam.chucker.R import com.chuckerteam.chucker.api.Chucker import com.chuckerteam.chucker.internal.data.entity.HttpTransaction import com.chuckerteam.chucker.internal.ui.BaseChuckerActivity +import java.util.HashSet internal class NotificationHelper(val context: Context) { From 62deabe1660eebe4c63323ffb15964f7c8e26593 Mon Sep 17 00:00:00 2001 From: jean Date: Thu, 12 Oct 2023 18:20:11 -0300 Subject: [PATCH 10/11] Fixing array objects tokens. --- .../transaction/TransactionPayloadAdapter.kt | 57 +++++++++++-------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadAdapter.kt b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadAdapter.kt index 200a933cc..5614f717d 100644 --- a/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadAdapter.kt +++ b/library/src/main/kotlin/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadAdapter.kt @@ -9,6 +9,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.text.getSpans +import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import com.chuckerteam.chucker.R import com.chuckerteam.chucker.databinding.ChuckerTransactionItemBodyCollapsableBinding @@ -248,7 +249,7 @@ internal sealed class TransactionPayloadViewHolder(view: View) : RecyclerView.Vi if (item !is TransactionPayloadItem.BodyCollapsableItem) return if (item.jsonElement == null) { - bodyBinding.clRoot.visibility = View.GONE + bodyBinding.clRoot.gone() return } @@ -256,8 +257,8 @@ internal sealed class TransactionPayloadViewHolder(view: View) : RecyclerView.Vi when { body.isJsonPrimitive -> { - bodyBinding.imgExpand.visibility = View.GONE - bodyBinding.rvSectionData.visibility = View.GONE + bodyBinding.imgExpand.gone() + bodyBinding.rvSectionData.gone() bodyBinding.txtStartValue.text = body.asString.plus(",") } @@ -277,7 +278,7 @@ internal sealed class TransactionPayloadViewHolder(view: View) : RecyclerView.Vi } } - bodyBinding.rvSectionData.visibility = View.VISIBLE + bodyBinding.rvSectionData.show() bodyBinding.rvSectionData.adapter = TransactionBodyAdapter().also { adapter -> adapter.setItems(attrList) } @@ -295,7 +296,7 @@ internal sealed class TransactionPayloadViewHolder(view: View) : RecyclerView.Vi val value: JsonElement = obj.get(key) ?: return val keyText = "\"" + key + "\"" - bodyBinding.imgExpand.visibility = View.GONE + bodyBinding.imgExpand.gone() bodyBinding.txtKey.text = keyText when { @@ -303,14 +304,14 @@ internal sealed class TransactionPayloadViewHolder(view: View) : RecyclerView.Vi val text = if (value.isJsonNull) "null" else "\"${value.asString}\"" bodyBinding.txtStartValue.text = text.plus(",") - bodyBinding.txtEndValue.visibility = View.GONE + bodyBinding.txtEndValue.gone() } value.isJsonObject -> { if (value.asJsonObject.isEmpty) { - bodyBinding.rvSectionData.visibility = View.GONE + bodyBinding.rvSectionData.gone() bodyBinding.txtStartValue.text = "{}," - bodyBinding.txtEndValue.visibility = View.GONE + bodyBinding.txtEndValue.gone() } else { bodyBinding.root.setClickForValue(element = value) } @@ -322,12 +323,15 @@ internal sealed class TransactionPayloadViewHolder(view: View) : RecyclerView.Vi } } else { // { "key1" : "value1", "key2" : "value2" } - bodyBinding.imgExpand.visibility = View.GONE - bodyBinding.txtKey.visibility = View.GONE - bodyBinding.txtDivider.visibility = View.GONE - bodyBinding.txtStartValue.visibility = View.GONE - bodyBinding.txtEndValue.visibility = View.GONE + bodyBinding.imgExpand.gone() + bodyBinding.txtKey.gone() + bodyBinding.txtDivider.gone() + + bodyBinding.txtStartValue.show() + bodyBinding.txtStartValue.text = "{" obj.showProperties() + bodyBinding.txtEndValue.show() + bodyBinding.txtEndValue.text = "}," } } @@ -336,12 +340,12 @@ internal sealed class TransactionPayloadViewHolder(view: View) : RecyclerView.Vi TransactionPayloadItem.BodyCollapsableItem(jsonElement = it.asJsonObject) }.also { list -> with(bodyBinding) { - imgExpand.visibility = View.GONE - txtKey.visibility = View.GONE - txtDivider.visibility = View.GONE - txtStartValue.visibility = View.GONE - txtEndValue.visibility = View.GONE - rvSectionData.visibility = View.VISIBLE + imgExpand.gone() + txtKey.gone() + txtDivider.gone() + txtStartValue.gone() + txtEndValue.gone() + rvSectionData.show() rvSectionData.adapter = TransactionBodyAdapter().also { adapter -> adapter.setItems(list) } @@ -352,9 +356,9 @@ internal sealed class TransactionPayloadViewHolder(view: View) : RecyclerView.Vi private fun View.setClickForValue(element: JsonElement) = with(bodyBinding) { var isOpen = false - imgExpand.visibility = View.VISIBLE + imgExpand.show() txtStartValue.text = if (element.isJsonObject) "{...}" else "[...]" - txtEndValue.visibility = View.GONE + txtEndValue.gone() setOnClickListener { view -> isOpen = isOpen.not() @@ -370,15 +374,15 @@ internal sealed class TransactionPayloadViewHolder(view: View) : RecyclerView.Vi view.isClickable = true if (isOpen) { - rvSectionData.visibility = View.VISIBLE + rvSectionData.show() txtStartValue.text = if (element.isJsonObject) "{" else "[" - txtEndValue.visibility = View.VISIBLE + txtEndValue.show() txtEndValue.text = if (element.isJsonObject) "}," else "]," } else { - rvSectionData.visibility = View.GONE + rvSectionData.gone() txtStartValue.text = if (element.isJsonObject) "{...}," else "[...]," - txtEndValue.visibility = View.GONE + txtEndValue.gone() } rvSectionData.adapter = TransactionBodyAdapter().also { adapter -> @@ -399,6 +403,9 @@ internal sealed class TransactionPayloadViewHolder(view: View) : RecyclerView.Vi } } + private fun View.show() = apply { isVisible = true } + private fun View.gone() = apply { isVisible = false } + internal companion object { const val OPEN_ROTATION_VALUE = 180f const val CLOSE_ROTATION_VALUE = -180f From dd0a396d437433bfeb8c8aa13a3ac7edad7f213b Mon Sep 17 00:00:00 2001 From: jean Date: Thu, 12 Oct 2023 18:24:28 -0300 Subject: [PATCH 11/11] Fixing lint. --- library/src/main/res/drawable/chuncker_ic_arrow_down.xml | 5 ----- .../res/layout/chucker_transaction_item_body_collapsable.xml | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) delete mode 100644 library/src/main/res/drawable/chuncker_ic_arrow_down.xml diff --git a/library/src/main/res/drawable/chuncker_ic_arrow_down.xml b/library/src/main/res/drawable/chuncker_ic_arrow_down.xml deleted file mode 100644 index 45adee71e..000000000 --- a/library/src/main/res/drawable/chuncker_ic_arrow_down.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/library/src/main/res/layout/chucker_transaction_item_body_collapsable.xml b/library/src/main/res/layout/chucker_transaction_item_body_collapsable.xml index 36ca4fb4a..86014f4df 100644 --- a/library/src/main/res/layout/chucker_transaction_item_body_collapsable.xml +++ b/library/src/main/res/layout/chucker_transaction_item_body_collapsable.xml @@ -24,7 +24,7 @@ android:visibility="gone" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:srcCompat="@drawable/chuncker_ic_arrow_down" + app:srcCompat="@drawable/chucker_ic_arrow_down" app:tint="@color/chucker_json_key_color" tools:visibility="visible" />