Skip to content

Commit

Permalink
Empty state for payloads (#325)
Browse files Browse the repository at this point in the history
* Add UI for empty payload state

* Replace emoticon icon with empty package

* Add empty state for request and response

* Add missing contentDescription

* Apply suggestions from code review

Co-Authored-By: Nicola Corti <corti.nico@gmail.com>

Co-authored-by: Nicola Corti <corti.nico@gmail.com>
  • Loading branch information
vbuberen and cortinico authored Apr 16, 2020
1 parent 6d5364d commit 272cd91
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,18 @@ import com.chuckerteam.chucker.internal.support.highlightWithDefinedColors
* We're using a [RecyclerView] to show the content of the body line by line to do not affect
* performances when loading big payloads.
*/
internal class TransactionBodyAdapter(
private val bodyItems: List<TransactionPayloadItem>
) : RecyclerView.Adapter<TransactionPayloadViewHolder>() {
internal class TransactionBodyAdapter : RecyclerView.Adapter<TransactionPayloadViewHolder>() {

private val items = arrayListOf<TransactionPayloadItem>()

fun setItems(bodyItems: List<TransactionPayloadItem>) {
items.clear()
items.addAll(bodyItems)
notifyDataSetChanged()
}

override fun onBindViewHolder(holder: TransactionPayloadViewHolder, position: Int) {
holder.bind(bodyItems[position])
holder.bind(items[position])
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransactionPayloadViewHolder {
Expand All @@ -46,18 +52,18 @@ internal class TransactionBodyAdapter(
}
}

override fun getItemCount() = bodyItems.size
override fun getItemCount() = items.size

override fun getItemViewType(position: Int): Int {
return when (bodyItems[position]) {
return when (items[position]) {
is TransactionPayloadItem.HeaderItem -> TYPE_HEADERS
is TransactionPayloadItem.BodyLineItem -> TYPE_BODY_LINE
is TransactionPayloadItem.ImageItem -> TYPE_IMAGE
}
}

internal fun highlightQueryWithColors(newText: String, backgroundColor: Int, foregroundColor: Int) {
bodyItems.filterIsInstance<TransactionPayloadItem.BodyLineItem>()
items.filterIsInstance<TransactionPayloadItem.BodyLineItem>()
.withIndex()
.forEach { (index, item) ->
if (item.line.contains(newText, ignoreCase = true)) {
Expand All @@ -77,7 +83,7 @@ internal class TransactionBodyAdapter(
}

internal fun resetHighlight() {
bodyItems.filterIsInstance<TransactionPayloadItem.BodyLineItem>()
items.filterIsInstance<TransactionPayloadItem.BodyLineItem>()
.withIndex()
.forEach { (index, item) ->
val spans = item.line.getSpans(0, item.line.length - 1, Any::class.java)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ internal class TransactionPayloadFragment :
Fragment(), SearchView.OnQueryTextListener {

private lateinit var payloadBinding: ChuckerFragmentTransactionPayloadBinding
private val payloadAdapter = TransactionBodyAdapter()

private var backgroundSpanColor: Int = Color.YELLOW
private var foregroundSpanColor: Int = Color.RED
Expand Down Expand Up @@ -72,21 +73,54 @@ internal class TransactionPayloadFragment :

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

payloadBinding.payloadRecyclerView.apply {
setHasFixedSize(true)
adapter = payloadAdapter
}

viewModel.transaction.observe(
viewLifecycleOwner,
Observer { transaction ->
if (transaction == null) return@Observer
uiScope.launch {
showProgress()
payloadBinding.loadingProgress.visibility = View.VISIBLE

val result = processPayload(type, transaction)
payloadBinding.responseRecyclerView.adapter = TransactionBodyAdapter(result)
payloadBinding.responseRecyclerView.setHasFixedSize(true)
hideProgress()
if (result.isEmpty()) {
showEmptyState()
} else {
payloadAdapter.setItems(result)
showPayloadState()
}
// Invalidating menu, because we need to hide menu items for empty payloads
requireActivity().invalidateOptionsMenu()

payloadBinding.loadingProgress.visibility = View.GONE
}
}
)
}

private fun showEmptyState() {
payloadBinding.apply {
emptyPayloadTextView.text = if (type == TYPE_RESPONSE) {
getString(R.string.chucker_response_is_empty)
} else {
getString(R.string.chucker_request_is_empty)
}
emptyStateGroup.visibility = View.VISIBLE
payloadRecyclerView.visibility = View.GONE
}
}

private fun showPayloadState() {
payloadBinding.apply {
emptyStateGroup.visibility = View.GONE
payloadRecyclerView.visibility = View.VISIBLE
}
}

override fun onDestroy() {
super.onDestroy()
uiScope.cancel()
Expand Down Expand Up @@ -184,30 +218,14 @@ internal class TransactionPayloadFragment :
override fun onQueryTextSubmit(query: String): Boolean = false

override fun onQueryTextChange(newText: String): Boolean {
val adapter = (payloadBinding.responseRecyclerView.adapter as TransactionBodyAdapter)
if (newText.isNotBlank() && newText.length > NUMBER_OF_IGNORED_SYMBOLS) {
adapter.highlightQueryWithColors(newText, backgroundSpanColor, foregroundSpanColor)
payloadAdapter.highlightQueryWithColors(newText, backgroundSpanColor, foregroundSpanColor)
} else {
adapter.resetHighlight()
payloadAdapter.resetHighlight()
}
return true
}

private fun showProgress() {
payloadBinding.apply {
loadingProgress.visibility = View.VISIBLE
responseRecyclerView.visibility = View.INVISIBLE
}
}

private fun hideProgress() {
payloadBinding.apply {
loadingProgress.visibility = View.INVISIBLE
responseRecyclerView.visibility = View.VISIBLE
requireActivity().invalidateOptionsMenu()
}
}

private suspend fun processPayload(
type: Int,
transaction: HttpTransaction
Expand Down Expand Up @@ -249,8 +267,10 @@ internal class TransactionPayloadFragment :
result.add(TransactionPayloadItem.BodyLineItem(SpannableStringBuilder.valueOf(it)))
}
} else {
bodyString.lines().forEach {
result.add(TransactionPayloadItem.BodyLineItem(SpannableStringBuilder.valueOf(it)))
if (bodyString.isNotBlank()) {
bodyString.lines().forEach {
result.add(TransactionPayloadItem.BodyLineItem(SpannableStringBuilder.valueOf(it)))
}
}
}
return@withContext result
Expand Down
10 changes: 10 additions & 0 deletions library/src/main/res/drawable/chucker_empty_payload.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#fff"
android:pathData="M2,10.96C1.5,10.68 1.35,10.07 1.63,9.59L3.13,7C3.24,6.8 3.41,6.66 3.6,6.58L11.43,2.18C11.59,2.06 11.79,2 12,2C12.21,2 12.41,2.06 12.57,2.18L20.47,6.62C20.66,6.72 20.82,6.88 20.91,7.08L22.36,9.6C22.64,10.08 22.47,10.69 22,10.96L21,11.54V16.5C21,16.88 20.79,17.21 20.47,17.38L12.57,21.82C12.41,21.94 12.21,22 12,22C11.79,22 11.59,21.94 11.43,21.82L3.53,17.38C3.21,17.21 3,16.88 3,16.5V10.96C2.7,11.13 2.32,11.14 2,10.96M12,4.15V4.15L12,10.85V10.85L17.96,7.5L12,4.15M5,15.91L11,19.29V12.58L5,9.21V15.91M19,15.91V12.69L14,15.59C13.67,15.77 13.3,15.76 13,15.6V19.29L19,15.91M13.85,13.36L20.13,9.73L19.55,8.72L13.27,12.35L13.85,13.36Z" />
</vector>
Original file line number Diff line number Diff line change
@@ -1,31 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
tools:context="com.chuckerteam.chucker.internal.ui.transaction.TransactionPayloadFragment">

<ProgressBar
<androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/loadingProgress"
style="@style/Widget.AppCompat.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true"
android:layout_gravity="center"
android:layout_margin="@dimen/chucker_doub_grid"
android:visibility="visible" />
android:indeterminate="true"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<ImageView
android:id="@+id/emptyPayloadImage"
android:layout_width="@dimen/chucker_octa_grid"
android:layout_height="@dimen/chucker_octa_grid"
android:layout_marginBottom="@dimen/chucker_quad_grid"
android:contentDescription="@string/chucker_body_empty"
android:src="@drawable/chucker_empty_payload"
android:tint="@color/chucker_color_primary"
app:layout_constraintBottom_toTopOf="@id/emptyPayloadTextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1" />

<TextView
android:id="@+id/emptyPayloadTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:text="@string/chucker_response_is_empty" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/responseRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
android:visibility="invisible"
android:id="@+id/payloadRecyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:clipToPadding="false"
android:paddingVertical="@dimen/chucker_doub_grid"
android:scrollbars="vertical"
android:visibility="invisible"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:listitem="@layout/chucker_transaction_item_body_line"
tools:visibility="visible"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
tools:visibility="visible" />

<androidx.constraintlayout.widget.Group
android:id="@+id/emptyStateGroup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:constraint_referenced_ids="emptyPayloadImage,emptyPayloadTextView"
tools:visibility="visible" />

</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
2 changes: 2 additions & 0 deletions library/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,6 @@
<string name="chucker_setup">Setup</string>
<string name="chucker_binary_data">binary data</string>
<string name="chucker_request_not_ready">The request isn\'t ready for sharing or saving</string>
<string name="chucker_request_is_empty">This request is empty</string>
<string name="chucker_response_is_empty">This response is empty</string>
</resources>

0 comments on commit 272cd91

Please sign in to comment.