From c4f45436c1fad4cb71d1ff7b7f5334df55f4ab4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sikora?= Date: Sun, 9 Feb 2020 21:29:33 +0100 Subject: [PATCH 01/10] Add switching between encoded and decoded URL formats. --- .../internal/data/entity/HttpTransaction.kt | 24 ++++- .../data/entity/HttpTransactionTuple.kt | 13 +++ .../chucker/internal/support/FormatUtils.kt | 4 +- .../chucker/internal/support/FormattedUrl.kt | 49 ++++++++++ .../chucker/internal/support/LiveDataUtils.kt | 34 +++++++ .../chucker/internal/ui/MainActivity.kt | 2 +- .../chucker/internal/ui/MainViewModel.kt | 20 ++-- .../ui/transaction/TransactionActivity.kt | 9 +- .../ui/transaction/TransactionAdapter.kt | 6 +- .../ui/transaction/TransactionListFragment.kt | 32 ++++-- .../TransactionOverviewFragment.kt | 2 +- .../ui/transaction/TransactionViewModel.kt | 14 ++- .../res/menu/chucker_transactions_list.xml | 7 +- library/src/main/res/values/strings.xml | 1 + .../internal/support/FormattedUrlTest.kt | 97 +++++++++++++++++++ .../internal/support/LiveDataUtilsTest.kt | 75 ++++++++++++++ 16 files changed, 356 insertions(+), 33 deletions(-) create mode 100644 library/src/main/java/com/chuckerteam/chucker/internal/support/FormattedUrl.kt create mode 100644 library/src/test/java/com/chuckerteam/chucker/internal/support/FormattedUrlTest.kt create mode 100644 library/src/test/java/com/chuckerteam/chucker/internal/support/LiveDataUtilsTest.kt diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/data/entity/HttpTransaction.kt b/library/src/main/java/com/chuckerteam/chucker/internal/data/entity/HttpTransaction.kt index a731f532b..b41fc46ea 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/data/entity/HttpTransaction.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/data/entity/HttpTransaction.kt @@ -9,6 +9,7 @@ import androidx.room.Entity import androidx.room.Ignore import androidx.room.PrimaryKey import com.chuckerteam.chucker.internal.support.FormatUtils +import com.chuckerteam.chucker.internal.support.FormattedUrl import com.chuckerteam.chucker.internal.support.JsonConverter import com.google.gson.reflect.TypeToken import java.util.Date @@ -46,7 +47,6 @@ internal class HttpTransaction( @ColumnInfo(name = "responseBody") var responseBody: String?, @ColumnInfo(name = "isResponseBodyPlainText") var isResponseBodyPlainText: Boolean = true, @ColumnInfo(name = "responseImageData") var responseImageData: ByteArray? - ) { @Ignore @@ -208,13 +208,27 @@ internal class HttpTransaction( } fun populateUrl(url: HttpUrl): HttpTransaction { - this.url = url.toString() - host = url.host() - path = ("/${url.pathSegments().joinToString("/")}${url.query()?.let { "?$it" } ?: ""}") - scheme = url.scheme() + val formattedUrl = FormattedUrl.fromHttpUrl(url, encoded = false) + this.url = formattedUrl.url + host = formattedUrl.host + path = formattedUrl.pathWithQuery + scheme = formattedUrl.scheme return this } + fun getFormattedUrl(encode: Boolean): String { + val url = this.url ?: return "" + val httpUrl = HttpUrl.get(url) + return FormattedUrl.fromHttpUrl(httpUrl, encode).url + } + + fun getFormattedPath(encode: Boolean): String { + val url = this.url ?: return "" + + val httpUrl = HttpUrl.parse(url) ?: return "" + return FormattedUrl.fromHttpUrl(httpUrl, encode).pathWithQuery + } + // Not relying on 'equals' because comparison be long due to request and response sizes // and it would be unwise to do this every time 'equals' is called. @Suppress("ComplexMethod") diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/data/entity/HttpTransactionTuple.kt b/library/src/main/java/com/chuckerteam/chucker/internal/data/entity/HttpTransactionTuple.kt index a48be2c95..c6d6589c1 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/data/entity/HttpTransactionTuple.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/data/entity/HttpTransactionTuple.kt @@ -2,6 +2,8 @@ package com.chuckerteam.chucker.internal.data.entity import androidx.room.ColumnInfo import com.chuckerteam.chucker.internal.support.FormatUtils +import com.chuckerteam.chucker.internal.support.FormattedUrl +import okhttp3.HttpUrl /** * A subset of [HttpTransaction] to perform faster Read operations on the Repository. @@ -42,4 +44,15 @@ internal class HttpTransactionTuple( private fun formatBytes(bytes: Long): String { return FormatUtils.formatByteCount(bytes, true) } + + fun getFormattedPath(encode: Boolean): String { + val path = this.path ?: return "" + + // Create dummy URL since there is no data in this class to get it from + // and we are only interested in a formatted path with query. + val dummyUrl = "https://www.example.com$path" + + val httpUrl = HttpUrl.parse(dummyUrl) ?: return "" + return FormattedUrl.fromHttpUrl(httpUrl, encode).pathWithQuery + } } diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/support/FormatUtils.kt b/library/src/main/java/com/chuckerteam/chucker/internal/support/FormatUtils.kt index e18d39ee0..c0c4184a4 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/support/FormatUtils.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/support/FormatUtils.kt @@ -94,8 +94,8 @@ internal object FormatUtils { } } - fun getShareText(context: Context, transaction: HttpTransaction): String { - var text = "${context.getString(R.string.chucker_url)}: ${transaction.url}\n" + fun getShareText(context: Context, transaction: HttpTransaction, encodeUrls: Boolean): String { + var text = "${context.getString(R.string.chucker_url)}: ${transaction.getFormattedUrl(encodeUrls)}\n" text += "${context.getString(R.string.chucker_method)}: ${transaction.method}\n" text += "${context.getString(R.string.chucker_protocol)}: ${transaction.protocol}\n" text += "${context.getString(R.string.chucker_status)}: ${transaction.status}\n" diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/support/FormattedUrl.kt b/library/src/main/java/com/chuckerteam/chucker/internal/support/FormattedUrl.kt new file mode 100644 index 000000000..3e133f933 --- /dev/null +++ b/library/src/main/java/com/chuckerteam/chucker/internal/support/FormattedUrl.kt @@ -0,0 +1,49 @@ +package com.chuckerteam.chucker.internal.support + +import okhttp3.HttpUrl + +internal class FormattedUrl private constructor( + val scheme: String, + val host: String, + val path: String, + val query: String +) { + val pathWithQuery: String + get() = if (query.isBlank()) { + path + } else { + "$path?$query" + } + + val url get() = "$scheme://$host$pathWithQuery" + + companion object { + fun fromHttpUrl(httpUrl: HttpUrl, encoded: Boolean): FormattedUrl { + return if (encoded) { + encodedUrl(httpUrl) + } else { + decodedUrl(httpUrl) + } + } + + private fun encodedUrl(httpUrl: HttpUrl): FormattedUrl { + val path = httpUrl.encodedPathSegments().joinToString("/") + return FormattedUrl( + httpUrl.scheme(), + httpUrl.host(), + if (path.isNotBlank()) "/$path" else "", + httpUrl.encodedQuery().orEmpty() + ) + } + + private fun decodedUrl(httpUrl: HttpUrl): FormattedUrl { + val path = httpUrl.pathSegments().joinToString("/") + return FormattedUrl( + httpUrl.scheme(), + httpUrl.host(), + if (path.isNotBlank()) "/$path" else "", + httpUrl.query().orEmpty() + ) + } + } +} diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/support/LiveDataUtils.kt b/library/src/main/java/com/chuckerteam/chucker/internal/support/LiveDataUtils.kt index 5b79194f6..129ab5c61 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/support/LiveDataUtils.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/support/LiveDataUtils.kt @@ -6,6 +6,40 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MediatorLiveData import java.util.concurrent.Executor +internal fun LiveData.combineLatest( + other: LiveData, + func: (T1, T2) -> R +): LiveData { + return MediatorLiveData().apply { + var lastA: T1? = null + var lastB: T2? = null + + addSource(this@combineLatest) { + lastA = it + val observedB = lastB + if (it == null && value != null) { + value = null + } else if (it != null && observedB != null) { + value = func(it, observedB) + } + } + + addSource(other) { + lastB = it + val observedA = lastA + if (it == null && value != null) { + value = null + } else if (observedA != null && it != null) { + value = func(observedA, it) + } + } + } +} + +internal fun LiveData.combineLatest(other: LiveData): LiveData> { + return combineLatest(other) { a, b -> a to b } +} + // Unlike built-in extension operation is performed on a provided thread pool. // This is needed in our case since we compare requests and responses which can be big // and result in frame drops. diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/MainActivity.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/MainActivity.kt index b85345213..fcfc2af09 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/MainActivity.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/MainActivity.kt @@ -76,7 +76,7 @@ internal class MainActivity : } override fun onTransactionClick(transactionId: Long, position: Int) { - TransactionActivity.start(this, transactionId) + TransactionActivity.start(this, transactionId, viewModel.encodeUrls.value == true) } companion object { diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/MainViewModel.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/MainViewModel.kt index 9fc4cec89..62fd0bf08 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/MainViewModel.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/MainViewModel.kt @@ -3,8 +3,8 @@ package com.chuckerteam.chucker.internal.ui import android.text.TextUtils import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.Transformations import androidx.lifecycle.ViewModel +import androidx.lifecycle.switchMap import com.chuckerteam.chucker.internal.data.entity.HttpTransactionTuple import com.chuckerteam.chucker.internal.data.entity.RecordedThrowableTuple import com.chuckerteam.chucker.internal.data.repository.RepositoryProvider @@ -14,7 +14,7 @@ internal class MainViewModel : ViewModel() { private val currentFilter = MutableLiveData("") - val transactions: LiveData> = Transformations.switchMap(currentFilter) { searchQuery -> + val transactions: LiveData> = currentFilter.switchMap { searchQuery -> with(RepositoryProvider.transaction()) { when { searchQuery.isNullOrBlank() -> { @@ -30,17 +30,21 @@ internal class MainViewModel : ViewModel() { } } - val errors: LiveData> = - Transformations.map( - RepositoryProvider.throwable().getSortedThrowablesTuples() - ) { - it - } + val errors: LiveData> = RepositoryProvider.throwable() + .getSortedThrowablesTuples() + + private val mutableEncodeUrls = MutableLiveData(false) + + val encodeUrls: LiveData = mutableEncodeUrls fun updateItemsFilter(searchQuery: String) { currentFilter.value = searchQuery } + fun encodeUrls(encode: Boolean) { + mutableEncodeUrls.value = encode + } + fun clearTransactions() { RepositoryProvider.transaction().deleteAllTransactions() NotificationHelper.clearBuffer() diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionActivity.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionActivity.kt index a8cb99c45..5adf12cfe 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionActivity.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionActivity.kt @@ -27,10 +27,11 @@ internal class TransactionActivity : BaseChuckerActivity() { setContentView(R.layout.chucker_activity_transaction) val transactionId = intent.getLongExtra(EXTRA_TRANSACTION_ID, 0) + val encodeUrls = intent.getBooleanExtra(EXTRA_ENCODE_URLS, false) // Create the instance now, so it can be shared by the // various fragments in the view pager later. - viewModel = ViewModelProvider(this, TransactionViewModelFactory(transactionId)) + viewModel = ViewModelProvider(this, TransactionViewModelFactory(transactionId, encodeUrls)) .get(TransactionViewModel::class.java) val toolbar = findViewById(R.id.toolbar) @@ -62,7 +63,7 @@ internal class TransactionActivity : BaseChuckerActivity() { when (item.itemId) { R.id.share_text -> { viewModel.transaction.value?.let { - share(getShareText(this, it)) + share(getShareText(this, it, viewModel.encodeUrls)) } ?: showToast(getString(R.string.chucker_request_not_ready)) true } @@ -101,11 +102,13 @@ internal class TransactionActivity : BaseChuckerActivity() { companion object { private const val MIME_TYPE = "text/plain" private const val EXTRA_TRANSACTION_ID = "transaction_id" + private const val EXTRA_ENCODE_URLS = "encode_ruls" private var selectedTabPosition = 0 - fun start(context: Context, transactionId: Long) { + fun start(context: Context, transactionId: Long, encodeUrls: Boolean) { val intent = Intent(context, TransactionActivity::class.java) intent.putExtra(EXTRA_TRANSACTION_ID, transactionId) + intent.putExtra(EXTRA_ENCODE_URLS, encodeUrls) context.startActivity(intent) } } diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionAdapter.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionAdapter.kt index 79cf7d160..42007670b 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionAdapter.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionAdapter.kt @@ -20,6 +20,7 @@ internal class TransactionAdapter internal constructor( private val listener: TransactionClickListListener? ) : RecyclerView.Adapter() { private var transactions: List = arrayListOf() + private var encodeUrls = false private val colorDefault: Int = ContextCompat.getColor(context, R.color.chucker_status_default) private val colorRequested: Int = ContextCompat.getColor(context, R.color.chucker_status_requested) @@ -40,7 +41,8 @@ internal class TransactionAdapter internal constructor( override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(transactions[position]) - fun setData(httpTransactions: List) { + fun setData(httpTransactions: List, encodeUrls: Boolean) { + this.encodeUrls = encodeUrls this.transactions = httpTransactions notifyDataSetChanged() } @@ -56,7 +58,7 @@ internal class TransactionAdapter internal constructor( @SuppressLint("SetTextI18n") fun bind(transaction: HttpTransactionTuple) { - path.text = "${transaction.method} ${transaction.path}" + path.text = "${transaction.method} ${transaction.getFormattedPath(encodeUrls)}" host.text = transaction.host start.text = DateFormat.getTimeInstance().format(transaction.requestDate) ssl.visibility = if (transaction.isSsl) View.VISIBLE else View.GONE diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionListFragment.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionListFragment.kt index 5d455cd47..8081112e8 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionListFragment.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionListFragment.kt @@ -17,6 +17,7 @@ import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.RecyclerView import com.chuckerteam.chucker.R +import com.chuckerteam.chucker.internal.support.combineLatest import com.chuckerteam.chucker.internal.ui.MainViewModel internal class TransactionListFragment : @@ -56,10 +57,11 @@ internal class TransactionListFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - viewModel.transactions.observe( + val transactionsWithEncoding = viewModel.transactions.combineLatest(viewModel.encodeUrls) + transactionsWithEncoding.observe( viewLifecycleOwner, - Observer { transactionTuples -> - adapter.setData(transactionTuples) + Observer { (transactionTuples, encode) -> + adapter.setData(transactionTuples, encode) tutorialView.visibility = if (transactionTuples.isEmpty()) View.VISIBLE else View.GONE } ) @@ -67,11 +69,28 @@ internal class TransactionListFragment : override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { inflater.inflate(R.menu.chucker_transactions_list, menu) + setUpSearch(menu) + setUpUrlEncoding(menu) + super.onCreateOptionsMenu(menu, inflater) + } + + private fun setUpSearch(menu: Menu) { val searchMenuItem = menu.findItem(R.id.search) val searchView = searchMenuItem.actionView as SearchView searchView.setOnQueryTextListener(this) searchView.setIconifiedByDefault(true) - super.onCreateOptionsMenu(menu, inflater) + } + + private fun setUpUrlEncoding(menu: Menu) { + val encodeUrlsMenuItem = menu.findItem(R.id.encode_urls) + encodeUrlsMenuItem.setOnMenuItemClickListener { item -> + viewModel.encodeUrls(!item.isChecked) + return@setOnMenuItemClickListener true + } + viewModel.encodeUrls.observe( + viewLifecycleOwner, + Observer { encode -> encodeUrlsMenuItem.isChecked = encode } + ) } override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -99,8 +118,9 @@ internal class TransactionListFragment : return true } - override fun onTransactionClick(transactionId: Long, position: Int) = - TransactionActivity.start(requireActivity(), transactionId) + override fun onTransactionClick(transactionId: Long, position: Int) { + TransactionActivity.start(requireActivity(), transactionId, viewModel.encodeUrls.value == true) + } companion object { fun newInstance(): TransactionListFragment { diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionOverviewFragment.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionOverviewFragment.kt index 1fc6039f9..3c5521c8d 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionOverviewFragment.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionOverviewFragment.kt @@ -67,7 +67,7 @@ internal class TransactionOverviewFragment : Fragment() { viewModel.transaction.observe( viewLifecycleOwner, Observer { transaction -> - url.text = transaction.url + url.text = transaction.getFormattedUrl(viewModel.encodeUrls) method.text = transaction.method protocol.text = transaction.protocol status.text = transaction.status.toString() diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt index 732c6fd90..2f1508b74 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt @@ -7,11 +7,14 @@ import androidx.lifecycle.ViewModelProvider import com.chuckerteam.chucker.internal.data.entity.HttpTransaction import com.chuckerteam.chucker.internal.data.repository.RepositoryProvider -internal class TransactionViewModel(transactionId: Long) : ViewModel() { +internal class TransactionViewModel( + transactionId: Long, + internal val encodeUrls: Boolean +) : ViewModel() { val transactionTitle: LiveData = Transformations.map(RepositoryProvider.transaction().getTransaction(transactionId)) { - if (it != null) "${it.method} ${it.path}" else "" + if (it != null) "${it.method} ${it.getFormattedPath(encodeUrls)}" else "" } val transaction: LiveData = Transformations.map(RepositoryProvider.transaction().getTransaction(transactionId)) { @@ -19,10 +22,13 @@ internal class TransactionViewModel(transactionId: Long) : ViewModel() { } } -internal class TransactionViewModelFactory(private val transactionId: Long = 0L) : +internal class TransactionViewModelFactory( + private val transactionId: Long = 0L, + private val encodeUrls: Boolean +) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { require(modelClass == TransactionViewModel::class.java) { "Cannot create $modelClass" } - return TransactionViewModel(transactionId) as T + return TransactionViewModel(transactionId, encodeUrls) as T } } diff --git a/library/src/main/res/menu/chucker_transactions_list.xml b/library/src/main/res/menu/chucker_transactions_list.xml index fecb4a941..854c2465d 100644 --- a/library/src/main/res/menu/chucker_transactions_list.xml +++ b/library/src/main/res/menu/chucker_transactions_list.xml @@ -10,4 +10,9 @@ android:id="@+id/clear" android:icon="@drawable/chucker_ic_delete_white" app:showAsAction="always" /> - \ No newline at end of file + + diff --git a/library/src/main/res/values/strings.xml b/library/src/main/res/values/strings.xml index c81ab68a1..305c10340 100644 --- a/library/src/main/res/values/strings.xml +++ b/library/src/main/res/values/strings.xml @@ -3,6 +3,7 @@ Chucker Recording HTTP activity Clear + Encode URLs Cancel Overview Request diff --git a/library/src/test/java/com/chuckerteam/chucker/internal/support/FormattedUrlTest.kt b/library/src/test/java/com/chuckerteam/chucker/internal/support/FormattedUrlTest.kt new file mode 100644 index 000000000..1d314415a --- /dev/null +++ b/library/src/test/java/com/chuckerteam/chucker/internal/support/FormattedUrlTest.kt @@ -0,0 +1,97 @@ +package com.chuckerteam.chucker.internal.support + +import junit.framework.TestCase.assertEquals +import okhttp3.HttpUrl +import org.junit.Test + +class FormattedUrlTest { + @Test + fun encodedUrl_withAllParams_isFormattedProperly() { + val url = HttpUrl.get("https://www.example.com/path/to some/resource?q=\"Hello, world!\"") + + val formattedUrl = FormattedUrl.fromHttpUrl(url, encoded = true) + + assertEquals("https", formattedUrl.scheme) + assertEquals("www.example.com", formattedUrl.host) + assertEquals("/path/to%20some/resource", formattedUrl.path) + assertEquals("q=%22Hello,%20world!%22", formattedUrl.query) + assertEquals("/path/to%20some/resource?q=%22Hello,%20world!%22", formattedUrl.pathWithQuery) + assertEquals( + "https://www.example.com/path/to%20some/resource?q=%22Hello,%20world!%22", + formattedUrl.url + ) + } + + @Test + fun encodedUrl_withoutPath_isFormattedProperly() { + val url = HttpUrl.get("https://www.example.com?q=\"Hello, world!\"") + + val formattedUrl = FormattedUrl.fromHttpUrl(url, encoded = true) + + assertEquals("https", formattedUrl.scheme) + assertEquals("www.example.com", formattedUrl.host) + assertEquals("", formattedUrl.path) + assertEquals("q=%22Hello,%20world!%22", formattedUrl.query) + assertEquals("?q=%22Hello,%20world!%22", formattedUrl.pathWithQuery) + assertEquals("https://www.example.com?q=%22Hello,%20world!%22", formattedUrl.url) + } + + @Test + fun encodedUrl_withoutQuery_isFormattedProperly() { + val url = HttpUrl.get("https://www.example.com/path/to some/resource") + + val formattedUrl = FormattedUrl.fromHttpUrl(url, encoded = true) + + assertEquals("https", formattedUrl.scheme) + assertEquals("www.example.com", formattedUrl.host) + assertEquals("/path/to%20some/resource", formattedUrl.path) + assertEquals("", formattedUrl.query) + assertEquals("/path/to%20some/resource", formattedUrl.pathWithQuery) + assertEquals("https://www.example.com/path/to%20some/resource", formattedUrl.url) + } + + @Test + fun decodedUrl_withAllParams_isFormattedProperly() { + val url = HttpUrl.get("https://www.example.com/path/to some/resource?q=\"Hello, world!\"") + + val formattedUrl = FormattedUrl.fromHttpUrl(url, encoded = false) + + assertEquals("https", formattedUrl.scheme) + assertEquals("www.example.com", formattedUrl.host) + assertEquals("/path/to some/resource", formattedUrl.path) + assertEquals("q=\"Hello, world!\"", formattedUrl.query) + assertEquals("/path/to some/resource?q=\"Hello, world!\"", formattedUrl.pathWithQuery) + assertEquals( + "https://www.example.com/path/to some/resource?q=\"Hello, world!\"", + formattedUrl.url + ) + } + + @Test + fun decodedUrl_withoutPath_isFormattedProperly() { + val url = HttpUrl.get("https://www.example.com?q=\"Hello, world!\"") + + val formattedUrl = FormattedUrl.fromHttpUrl(url, encoded = false) + + assertEquals("https", formattedUrl.scheme) + assertEquals("www.example.com", formattedUrl.host) + assertEquals("", formattedUrl.path) + assertEquals("q=\"Hello, world!\"", formattedUrl.query) + assertEquals("?q=\"Hello, world!\"", formattedUrl.pathWithQuery) + assertEquals("https://www.example.com?q=\"Hello, world!\"", formattedUrl.url) + } + + @Test + fun decodedUrl_withoutQuery_isFormattedProperly() { + val url = HttpUrl.get("https://www.example.com/path/to some/resource") + + val formattedUrl = FormattedUrl.fromHttpUrl(url, encoded = false) + + assertEquals("https", formattedUrl.scheme) + assertEquals("www.example.com", formattedUrl.host) + assertEquals("/path/to some/resource", formattedUrl.path) + assertEquals("", formattedUrl.query) + assertEquals("/path/to some/resource", formattedUrl.pathWithQuery) + assertEquals("https://www.example.com/path/to some/resource", formattedUrl.url) + } +} diff --git a/library/src/test/java/com/chuckerteam/chucker/internal/support/LiveDataUtilsTest.kt b/library/src/test/java/com/chuckerteam/chucker/internal/support/LiveDataUtilsTest.kt new file mode 100644 index 000000000..19ddd37c1 --- /dev/null +++ b/library/src/test/java/com/chuckerteam/chucker/internal/support/LiveDataUtilsTest.kt @@ -0,0 +1,75 @@ +package com.chuckerteam.chucker.internal.support + +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.Observer +import junit.framework.TestCase.assertEquals +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class LiveDataUtilsTest { + @get:Rule val instantExecutorRule = InstantTaskExecutorRule() + + private val liveDataA = MutableLiveData() + private val liveDataB = MutableLiveData() + + private val combinedData = liveDataA.combineLatest(liveDataB) + private val results = mutableListOf>() + private val observer = Observer> { results += it } + + @Before + fun setUp() { + combinedData.observeForever(observer) + } + + @After + fun tearDown() { + combinedData.removeObserver(observer) + results.clear() + } + + @Test + fun firstEmptyValue_preventsDownstreamEmissions() { + liveDataB.value = 1 + liveDataB.value = 2 + liveDataB.value = 3 + + assertEquals(emptyList>(), results) + } + + @Test + fun secondEmptyValue_preventsDownstreamEmissions() { + liveDataA.value = true + liveDataA.value = false + + assertEquals(emptyList>(), results) + } + + @Test + fun bothEmittedValues_areCombinedDownstream() { + liveDataA.value = true + liveDataB.value = 1 + + assertEquals(listOf(true to 1), results) + } + + @Test + fun lastFirstValue_isCombinedWithNewestSecondValues() { + liveDataA.value = true + liveDataB.value = 1 + liveDataB.value = 2 + + assertEquals(listOf(true to 1, true to 2), results) + } + + @Test + fun lastSecondValue_isCombinedWithNewestFirstValues() { + liveDataA.value = true + liveDataB.value = 1 + liveDataA.value = false + + assertEquals(listOf(true to 1, false to 1), results) + } +} From 635ba8317c614a41b0ab39ae522100776f04cc8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sikora?= Date: Sun, 9 Feb 2020 21:42:05 +0100 Subject: [PATCH 02/10] Save URL preference persistently. --- .../main/java/com/chuckerteam/chucker/api/Chucker.kt | 7 +++++++ .../com/chuckerteam/chucker/api/RetentionManager.kt | 3 +-- .../ui/transaction/TransactionListFragment.kt | 11 ++++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/library/src/main/java/com/chuckerteam/chucker/api/Chucker.kt b/library/src/main/java/com/chuckerteam/chucker/api/Chucker.kt index 203d15003..768915299 100644 --- a/library/src/main/java/com/chuckerteam/chucker/api/Chucker.kt +++ b/library/src/main/java/com/chuckerteam/chucker/api/Chucker.kt @@ -2,6 +2,7 @@ package com.chuckerteam.chucker.api import android.content.Context import android.content.Intent +import android.content.SharedPreferences import androidx.annotation.IntDef import com.chuckerteam.chucker.internal.support.ChuckerCrashHandler import com.chuckerteam.chucker.internal.support.NotificationHelper @@ -68,5 +69,11 @@ object Chucker { @IntDef(value = [SCREEN_HTTP, SCREEN_ERROR]) annotation class Screen + internal fun chuckerPreferences(context: Context): SharedPreferences { + return context.getSharedPreferences(PREFS_NAME, 0) + } + internal const val LOG_TAG = "Chucker" + + private const val PREFS_NAME = "chucker_preferences" } diff --git a/library/src/main/java/com/chuckerteam/chucker/api/RetentionManager.kt b/library/src/main/java/com/chuckerteam/chucker/api/RetentionManager.kt index ef0436de8..78c2b8ab0 100644 --- a/library/src/main/java/com/chuckerteam/chucker/api/RetentionManager.kt +++ b/library/src/main/java/com/chuckerteam/chucker/api/RetentionManager.kt @@ -23,7 +23,7 @@ class RetentionManager @JvmOverloads constructor( private val period: Long = toMillis(retentionPeriod) // How often the cleanup should happen private val cleanupFrequency: Long - private val prefs: SharedPreferences = context.getSharedPreferences(PREFS_NAME, 0) + private val prefs: SharedPreferences = Chucker.chuckerPreferences(context) init { cleanupFrequency = if (retentionPeriod == Period.ONE_HOUR) { @@ -91,7 +91,6 @@ class RetentionManager @JvmOverloads constructor( } companion object { - private const val PREFS_NAME = "chucker_preferences" private const val KEY_LAST_CLEANUP = "last_cleanup" private var lastCleanup: Long = 0 } diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionListFragment.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionListFragment.kt index 8081112e8..22f5a1a4f 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionListFragment.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionListFragment.kt @@ -1,5 +1,6 @@ package com.chuckerteam.chucker.internal.ui.transaction +import android.content.SharedPreferences import android.os.Bundle import android.text.method.LinkMovementMethod import android.view.LayoutInflater @@ -17,6 +18,7 @@ import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.RecyclerView import com.chuckerteam.chucker.R +import com.chuckerteam.chucker.api.Chucker import com.chuckerteam.chucker.internal.support.combineLatest import com.chuckerteam.chucker.internal.ui.MainViewModel @@ -28,11 +30,13 @@ internal class TransactionListFragment : private lateinit var viewModel: MainViewModel private lateinit var adapter: TransactionAdapter private lateinit var tutorialView: View + private lateinit var preferences: SharedPreferences override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) viewModel = ViewModelProvider(requireActivity())[MainViewModel::class.java] + preferences = Chucker.chuckerPreferences(requireContext()) } override fun onCreateView( @@ -84,13 +88,16 @@ internal class TransactionListFragment : private fun setUpUrlEncoding(menu: Menu) { val encodeUrlsMenuItem = menu.findItem(R.id.encode_urls) encodeUrlsMenuItem.setOnMenuItemClickListener { item -> - viewModel.encodeUrls(!item.isChecked) + val encode = !item.isChecked + preferences.edit().putBoolean(ENABLE_URL_ENCODING, encode).apply() + viewModel.encodeUrls(encode) return@setOnMenuItemClickListener true } viewModel.encodeUrls.observe( viewLifecycleOwner, Observer { encode -> encodeUrlsMenuItem.isChecked = encode } ) + viewModel.encodeUrls(preferences.getBoolean(ENABLE_URL_ENCODING, false)) } override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -123,6 +130,8 @@ internal class TransactionListFragment : } companion object { + private const val ENABLE_URL_ENCODING = "enable_url_encoding" + fun newInstance(): TransactionListFragment { return TransactionListFragment() } From 718ab74daae7037823d9c331a762fe6d59535a38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sikora?= Date: Sat, 15 Feb 2020 10:54:45 +0100 Subject: [PATCH 03/10] Revert "Save URL preference persistently." This reverts commit 69e7b485 --- .../main/java/com/chuckerteam/chucker/api/Chucker.kt | 7 ------- .../com/chuckerteam/chucker/api/RetentionManager.kt | 3 ++- .../ui/transaction/TransactionListFragment.kt | 11 +---------- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/library/src/main/java/com/chuckerteam/chucker/api/Chucker.kt b/library/src/main/java/com/chuckerteam/chucker/api/Chucker.kt index 768915299..203d15003 100644 --- a/library/src/main/java/com/chuckerteam/chucker/api/Chucker.kt +++ b/library/src/main/java/com/chuckerteam/chucker/api/Chucker.kt @@ -2,7 +2,6 @@ package com.chuckerteam.chucker.api import android.content.Context import android.content.Intent -import android.content.SharedPreferences import androidx.annotation.IntDef import com.chuckerteam.chucker.internal.support.ChuckerCrashHandler import com.chuckerteam.chucker.internal.support.NotificationHelper @@ -69,11 +68,5 @@ object Chucker { @IntDef(value = [SCREEN_HTTP, SCREEN_ERROR]) annotation class Screen - internal fun chuckerPreferences(context: Context): SharedPreferences { - return context.getSharedPreferences(PREFS_NAME, 0) - } - internal const val LOG_TAG = "Chucker" - - private const val PREFS_NAME = "chucker_preferences" } diff --git a/library/src/main/java/com/chuckerteam/chucker/api/RetentionManager.kt b/library/src/main/java/com/chuckerteam/chucker/api/RetentionManager.kt index 78c2b8ab0..ef0436de8 100644 --- a/library/src/main/java/com/chuckerteam/chucker/api/RetentionManager.kt +++ b/library/src/main/java/com/chuckerteam/chucker/api/RetentionManager.kt @@ -23,7 +23,7 @@ class RetentionManager @JvmOverloads constructor( private val period: Long = toMillis(retentionPeriod) // How often the cleanup should happen private val cleanupFrequency: Long - private val prefs: SharedPreferences = Chucker.chuckerPreferences(context) + private val prefs: SharedPreferences = context.getSharedPreferences(PREFS_NAME, 0) init { cleanupFrequency = if (retentionPeriod == Period.ONE_HOUR) { @@ -91,6 +91,7 @@ class RetentionManager @JvmOverloads constructor( } companion object { + private const val PREFS_NAME = "chucker_preferences" private const val KEY_LAST_CLEANUP = "last_cleanup" private var lastCleanup: Long = 0 } diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionListFragment.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionListFragment.kt index 22f5a1a4f..8081112e8 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionListFragment.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionListFragment.kt @@ -1,6 +1,5 @@ package com.chuckerteam.chucker.internal.ui.transaction -import android.content.SharedPreferences import android.os.Bundle import android.text.method.LinkMovementMethod import android.view.LayoutInflater @@ -18,7 +17,6 @@ import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.RecyclerView import com.chuckerteam.chucker.R -import com.chuckerteam.chucker.api.Chucker import com.chuckerteam.chucker.internal.support.combineLatest import com.chuckerteam.chucker.internal.ui.MainViewModel @@ -30,13 +28,11 @@ internal class TransactionListFragment : private lateinit var viewModel: MainViewModel private lateinit var adapter: TransactionAdapter private lateinit var tutorialView: View - private lateinit var preferences: SharedPreferences override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) viewModel = ViewModelProvider(requireActivity())[MainViewModel::class.java] - preferences = Chucker.chuckerPreferences(requireContext()) } override fun onCreateView( @@ -88,16 +84,13 @@ internal class TransactionListFragment : private fun setUpUrlEncoding(menu: Menu) { val encodeUrlsMenuItem = menu.findItem(R.id.encode_urls) encodeUrlsMenuItem.setOnMenuItemClickListener { item -> - val encode = !item.isChecked - preferences.edit().putBoolean(ENABLE_URL_ENCODING, encode).apply() - viewModel.encodeUrls(encode) + viewModel.encodeUrls(!item.isChecked) return@setOnMenuItemClickListener true } viewModel.encodeUrls.observe( viewLifecycleOwner, Observer { encode -> encodeUrlsMenuItem.isChecked = encode } ) - viewModel.encodeUrls(preferences.getBoolean(ENABLE_URL_ENCODING, false)) } override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -130,8 +123,6 @@ internal class TransactionListFragment : } companion object { - private const val ENABLE_URL_ENCODING = "enable_url_encoding" - fun newInstance(): TransactionListFragment { return TransactionListFragment() } From 0d926640613390a70067f137fc9ee4e97e9fab50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sikora?= Date: Sat, 15 Feb 2020 11:56:08 +0100 Subject: [PATCH 04/10] Make URL encoding transaction specific. --- .../HttpTransactionDatabaseRepository.kt | 5 ++- .../repository/HttpTransactionRepository.kt | 2 +- .../internal/data/room/HttpTransactionDao.kt | 2 +- .../chucker/internal/ui/MainActivity.kt | 2 +- .../chucker/internal/ui/MainViewModel.kt | 8 ---- .../ui/transaction/TransactionActivity.kt | 29 ++++++++++--- .../ui/transaction/TransactionAdapter.kt | 6 +-- .../ui/transaction/TransactionListFragment.kt | 23 ++-------- .../TransactionOverviewFragment.kt | 42 ++++++++++--------- .../transaction/TransactionPayloadFragment.kt | 3 ++ .../ui/transaction/TransactionViewModel.kt | 35 ++++++++++------ .../drawable/chucker_ic_decoded_url_white.xml | 10 +++++ .../drawable/chucker_ic_encoded_url_white.xml | 11 +++++ .../res/drawable/chucker_ic_save_white.xml | 2 +- .../src/main/res/menu/chucker_transaction.xml | 9 +++- .../res/menu/chucker_transactions_list.xml | 5 --- library/src/main/res/values/strings.xml | 2 +- 17 files changed, 114 insertions(+), 82 deletions(-) create mode 100644 library/src/main/res/drawable/chucker_ic_decoded_url_white.xml create mode 100644 library/src/main/res/drawable/chucker_ic_encoded_url_white.xml diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/data/repository/HttpTransactionDatabaseRepository.kt b/library/src/main/java/com/chuckerteam/chucker/internal/data/repository/HttpTransactionDatabaseRepository.kt index 385061db1..ae5f41c1a 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/data/repository/HttpTransactionDatabaseRepository.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/data/repository/HttpTransactionDatabaseRepository.kt @@ -19,8 +19,9 @@ internal class HttpTransactionDatabaseRepository(private val database: ChuckerDa return transcationDao.getFilteredTuples("$code%", pathQuery) } - override fun getTransaction(transactionId: Long): LiveData { - return transcationDao.getById(transactionId).distinctUntilChanged { old, new -> old.hasTheSameContent(new) } + override fun getTransaction(transactionId: Long): LiveData { + return transcationDao.getById(transactionId) + .distinctUntilChanged { old, new -> old?.hasTheSameContent(new) != false } } override fun getSortedTransactionTuples(): LiveData> { diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/data/repository/HttpTransactionRepository.kt b/library/src/main/java/com/chuckerteam/chucker/internal/data/repository/HttpTransactionRepository.kt index 89b40189c..86c73e539 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/data/repository/HttpTransactionRepository.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/data/repository/HttpTransactionRepository.kt @@ -23,5 +23,5 @@ internal interface HttpTransactionRepository { fun getFilteredTransactionTuples(code: String, path: String): LiveData> - fun getTransaction(transactionId: Long): LiveData + fun getTransaction(transactionId: Long): LiveData } diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/data/room/HttpTransactionDao.kt b/library/src/main/java/com/chuckerteam/chucker/internal/data/room/HttpTransactionDao.kt index 8b7bcaee9..eabb5bcab 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/data/room/HttpTransactionDao.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/data/room/HttpTransactionDao.kt @@ -37,7 +37,7 @@ internal interface HttpTransactionDao { fun deleteAll() @Query("SELECT * FROM transactions WHERE id = :id") - fun getById(id: Long): LiveData + fun getById(id: Long): LiveData @Query("DELETE FROM transactions WHERE requestDate <= :threshold") fun deleteBefore(threshold: Long) diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/MainActivity.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/MainActivity.kt index fcfc2af09..b85345213 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/MainActivity.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/MainActivity.kt @@ -76,7 +76,7 @@ internal class MainActivity : } override fun onTransactionClick(transactionId: Long, position: Int) { - TransactionActivity.start(this, transactionId, viewModel.encodeUrls.value == true) + TransactionActivity.start(this, transactionId) } companion object { diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/MainViewModel.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/MainViewModel.kt index 62fd0bf08..51fe6f208 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/MainViewModel.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/MainViewModel.kt @@ -33,18 +33,10 @@ internal class MainViewModel : ViewModel() { val errors: LiveData> = RepositoryProvider.throwable() .getSortedThrowablesTuples() - private val mutableEncodeUrls = MutableLiveData(false) - - val encodeUrls: LiveData = mutableEncodeUrls - fun updateItemsFilter(searchQuery: String) { currentFilter.value = searchQuery } - fun encodeUrls(encode: Boolean) { - mutableEncodeUrls.value = encode - } - fun clearTransactions() { RepositoryProvider.transaction().deleteAllTransactions() NotificationHelper.clearBuffer() diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionActivity.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionActivity.kt index 5adf12cfe..685f2cc4d 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionActivity.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionActivity.kt @@ -27,11 +27,10 @@ internal class TransactionActivity : BaseChuckerActivity() { setContentView(R.layout.chucker_activity_transaction) val transactionId = intent.getLongExtra(EXTRA_TRANSACTION_ID, 0) - val encodeUrls = intent.getBooleanExtra(EXTRA_ENCODE_URLS, false) // Create the instance now, so it can be shared by the // various fragments in the view pager later. - viewModel = ViewModelProvider(this, TransactionViewModelFactory(transactionId, encodeUrls)) + viewModel = ViewModelProvider(this, TransactionViewModelFactory(transactionId)) .get(TransactionViewModel::class.java) val toolbar = findViewById(R.id.toolbar) @@ -56,14 +55,34 @@ internal class TransactionActivity : BaseChuckerActivity() { override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.chucker_transaction, menu) + setUpUrlEncoding(menu) return super.onCreateOptionsMenu(menu) } + private fun setUpUrlEncoding(menu: Menu) { + val encodeUrlMenuItem = menu.findItem(R.id.encode_url) + encodeUrlMenuItem.setOnMenuItemClickListener { + viewModel.switchUrlEncoding() + return@setOnMenuItemClickListener true + } + viewModel.encodeUrl.observe( + this, + Observer { encode -> + val icon = if (encode) { + R.drawable.chucker_ic_encoded_url_white + } else { + R.drawable.chucker_ic_decoded_url_white + } + encodeUrlMenuItem.setIcon(icon) + } + ) + } + override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { R.id.share_text -> { viewModel.transaction.value?.let { - share(getShareText(this, it, viewModel.encodeUrls)) + share(getShareText(this, it, viewModel.encodeUrl.value!!)) } ?: showToast(getString(R.string.chucker_request_not_ready)) true } @@ -102,13 +121,11 @@ internal class TransactionActivity : BaseChuckerActivity() { companion object { private const val MIME_TYPE = "text/plain" private const val EXTRA_TRANSACTION_ID = "transaction_id" - private const val EXTRA_ENCODE_URLS = "encode_ruls" private var selectedTabPosition = 0 - fun start(context: Context, transactionId: Long, encodeUrls: Boolean) { + fun start(context: Context, transactionId: Long) { val intent = Intent(context, TransactionActivity::class.java) intent.putExtra(EXTRA_TRANSACTION_ID, transactionId) - intent.putExtra(EXTRA_ENCODE_URLS, encodeUrls) context.startActivity(intent) } } diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionAdapter.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionAdapter.kt index 42007670b..c716a74be 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionAdapter.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionAdapter.kt @@ -20,7 +20,6 @@ internal class TransactionAdapter internal constructor( private val listener: TransactionClickListListener? ) : RecyclerView.Adapter() { private var transactions: List = arrayListOf() - private var encodeUrls = false private val colorDefault: Int = ContextCompat.getColor(context, R.color.chucker_status_default) private val colorRequested: Int = ContextCompat.getColor(context, R.color.chucker_status_requested) @@ -41,8 +40,7 @@ internal class TransactionAdapter internal constructor( override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(transactions[position]) - fun setData(httpTransactions: List, encodeUrls: Boolean) { - this.encodeUrls = encodeUrls + fun setData(httpTransactions: List) { this.transactions = httpTransactions notifyDataSetChanged() } @@ -58,7 +56,7 @@ internal class TransactionAdapter internal constructor( @SuppressLint("SetTextI18n") fun bind(transaction: HttpTransactionTuple) { - path.text = "${transaction.method} ${transaction.getFormattedPath(encodeUrls)}" + path.text = "${transaction.method} ${transaction.getFormattedPath(encode = false)}" host.text = transaction.host start.text = DateFormat.getTimeInstance().format(transaction.requestDate) ssl.visibility = if (transaction.isSsl) View.VISIBLE else View.GONE diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionListFragment.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionListFragment.kt index 8081112e8..c26853a05 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionListFragment.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionListFragment.kt @@ -17,7 +17,6 @@ import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.RecyclerView import com.chuckerteam.chucker.R -import com.chuckerteam.chucker.internal.support.combineLatest import com.chuckerteam.chucker.internal.ui.MainViewModel internal class TransactionListFragment : @@ -57,11 +56,10 @@ internal class TransactionListFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val transactionsWithEncoding = viewModel.transactions.combineLatest(viewModel.encodeUrls) - transactionsWithEncoding.observe( + viewModel.transactions.observe( viewLifecycleOwner, - Observer { (transactionTuples, encode) -> - adapter.setData(transactionTuples, encode) + Observer { transactionTuples -> + adapter.setData(transactionTuples) tutorialView.visibility = if (transactionTuples.isEmpty()) View.VISIBLE else View.GONE } ) @@ -70,7 +68,6 @@ internal class TransactionListFragment : override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { inflater.inflate(R.menu.chucker_transactions_list, menu) setUpSearch(menu) - setUpUrlEncoding(menu) super.onCreateOptionsMenu(menu, inflater) } @@ -81,18 +78,6 @@ internal class TransactionListFragment : searchView.setIconifiedByDefault(true) } - private fun setUpUrlEncoding(menu: Menu) { - val encodeUrlsMenuItem = menu.findItem(R.id.encode_urls) - encodeUrlsMenuItem.setOnMenuItemClickListener { item -> - viewModel.encodeUrls(!item.isChecked) - return@setOnMenuItemClickListener true - } - viewModel.encodeUrls.observe( - viewLifecycleOwner, - Observer { encode -> encodeUrlsMenuItem.isChecked = encode } - ) - } - override fun onOptionsItemSelected(item: MenuItem): Boolean { return if (item.itemId == R.id.clear) { AlertDialog.Builder(requireContext()) @@ -119,7 +104,7 @@ internal class TransactionListFragment : } override fun onTransactionClick(transactionId: Long, position: Int) { - TransactionActivity.start(requireActivity(), transactionId, viewModel.encodeUrls.value == true) + TransactionActivity.start(requireActivity(), transactionId) } companion object { diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionOverviewFragment.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionOverviewFragment.kt index 3c5521c8d..2f3d7e2f8 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionOverviewFragment.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionOverviewFragment.kt @@ -11,6 +11,7 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import com.chuckerteam.chucker.R +import com.chuckerteam.chucker.internal.support.combineLatest internal class TransactionOverviewFragment : Fragment() { @@ -56,30 +57,33 @@ internal class TransactionOverviewFragment : Fragment() { } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - val saveMenuItem = menu.findItem(R.id.save_body) - saveMenuItem.isVisible = false + menu.findItem(R.id.save_body).isVisible = false + menu.findItem(R.id.encode_url).isVisible = true super.onCreateOptionsMenu(menu, inflater) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - viewModel.transaction.observe( - viewLifecycleOwner, - Observer { transaction -> - url.text = transaction.getFormattedUrl(viewModel.encodeUrls) - method.text = transaction.method - protocol.text = transaction.protocol - status.text = transaction.status.toString() - response.text = transaction.responseSummaryText - ssl.setText(if (transaction.isSsl) R.string.chucker_yes else R.string.chucker_no) - requestTime.text = transaction.requestDateString - responseTime.text = transaction.responseDateString - duration.text = transaction.durationString - requestSize.text = transaction.requestSizeString - responseSize.text = transaction.responseSizeString - totalSize.text = transaction.totalSizeString - } - ) + + viewModel.transaction + .combineLatest(viewModel.encodeUrl) + .observe( + viewLifecycleOwner, + Observer { (transaction, encodeUrl) -> + url.text = transaction?.getFormattedUrl(encodeUrl) + method.text = transaction?.method + protocol.text = transaction?.protocol + status.text = transaction?.status?.toString() + response.text = transaction?.responseSummaryText + ssl.setText(if (transaction?.isSsl == true) R.string.chucker_yes else R.string.chucker_no) + requestTime.text = transaction?.requestDateString + responseTime.text = transaction?.responseDateString + duration.text = transaction?.durationString + requestSize.text = transaction?.requestSizeString + responseSize.text = transaction?.responseSizeString + totalSize.text = transaction?.totalSizeString + } + ) } } diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt index 93312550e..ce83b583d 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionPayloadFragment.kt @@ -71,6 +71,7 @@ internal class TransactionPayloadFragment : viewModel.transaction.observe( viewLifecycleOwner, Observer { transaction -> + if (transaction == null) return@Observer PayloadLoaderTask(this).execute(Pair(type, transaction)) } ) @@ -104,6 +105,8 @@ internal class TransactionPayloadFragment : } } + menu.findItem(R.id.encode_url).isVisible = false + super.onCreateOptionsMenu(menu, inflater) } diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt index 2f1508b74..a25e528c2 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt @@ -1,34 +1,43 @@ package com.chuckerteam.chucker.internal.ui.transaction import androidx.lifecycle.LiveData -import androidx.lifecycle.Transformations +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.chuckerteam.chucker.internal.data.entity.HttpTransaction import com.chuckerteam.chucker.internal.data.repository.RepositoryProvider +import com.chuckerteam.chucker.internal.support.combineLatest internal class TransactionViewModel( - transactionId: Long, - internal val encodeUrls: Boolean + transactionId: Long ) : ViewModel() { - val transactionTitle: LiveData = - Transformations.map(RepositoryProvider.transaction().getTransaction(transactionId)) { - if (it != null) "${it.method} ${it.getFormattedPath(encodeUrls)}" else "" - } - val transaction: LiveData = - Transformations.map(RepositoryProvider.transaction().getTransaction(transactionId)) { - it + private val mutableEncodeUrl = MutableLiveData(false) + + val encodeUrl: LiveData = mutableEncodeUrl + + val transactionTitle: LiveData = RepositoryProvider.transaction() + .getTransaction(transactionId) + .combineLatest(encodeUrl) { transaction, encodeUrl -> + if (transaction != null) "${transaction.method} ${transaction.getFormattedPath(encode = encodeUrl)}" else "" } + + val transaction: LiveData = RepositoryProvider.transaction().getTransaction(transactionId) + + fun switchUrlEncoding() = encodeUrl(encodeUrl.value!!) + + fun encodeUrl(encode: Boolean) { + mutableEncodeUrl.value = encode + } } internal class TransactionViewModelFactory( - private val transactionId: Long = 0L, - private val encodeUrls: Boolean + private val transactionId: Long = 0L ) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { require(modelClass == TransactionViewModel::class.java) { "Cannot create $modelClass" } - return TransactionViewModel(transactionId, encodeUrls) as T + @Suppress("UNCHECKED_CAST") + return TransactionViewModel(transactionId) as T } } diff --git a/library/src/main/res/drawable/chucker_ic_decoded_url_white.xml b/library/src/main/res/drawable/chucker_ic_decoded_url_white.xml new file mode 100644 index 000000000..043a11f63 --- /dev/null +++ b/library/src/main/res/drawable/chucker_ic_decoded_url_white.xml @@ -0,0 +1,10 @@ + + + diff --git a/library/src/main/res/drawable/chucker_ic_encoded_url_white.xml b/library/src/main/res/drawable/chucker_ic_encoded_url_white.xml new file mode 100644 index 000000000..1da285872 --- /dev/null +++ b/library/src/main/res/drawable/chucker_ic_encoded_url_white.xml @@ -0,0 +1,11 @@ + + + + diff --git a/library/src/main/res/drawable/chucker_ic_save_white.xml b/library/src/main/res/drawable/chucker_ic_save_white.xml index 56e36736f..b728b571b 100644 --- a/library/src/main/res/drawable/chucker_ic_save_white.xml +++ b/library/src/main/res/drawable/chucker_ic_save_white.xml @@ -6,4 +6,4 @@ - \ No newline at end of file + diff --git a/library/src/main/res/menu/chucker_transaction.xml b/library/src/main/res/menu/chucker_transaction.xml index 4d724d784..a91874c0f 100644 --- a/library/src/main/res/menu/chucker_transaction.xml +++ b/library/src/main/res/menu/chucker_transaction.xml @@ -8,6 +8,13 @@ android:visible="false" app:actionViewClass="androidx.appcompat.widget.SearchView" app:showAsAction="collapseActionView|ifRoom" /> + + - \ No newline at end of file + diff --git a/library/src/main/res/menu/chucker_transactions_list.xml b/library/src/main/res/menu/chucker_transactions_list.xml index 854c2465d..36f7203dd 100644 --- a/library/src/main/res/menu/chucker_transactions_list.xml +++ b/library/src/main/res/menu/chucker_transactions_list.xml @@ -10,9 +10,4 @@ android:id="@+id/clear" android:icon="@drawable/chucker_ic_delete_white" app:showAsAction="always" /> - diff --git a/library/src/main/res/values/strings.xml b/library/src/main/res/values/strings.xml index 305c10340..aec0c8129 100644 --- a/library/src/main/res/values/strings.xml +++ b/library/src/main/res/values/strings.xml @@ -3,7 +3,7 @@ Chucker Recording HTTP activity Clear - Encode URLs + Encode URL Cancel Overview Request From d1c263b50d3c02e63fa7c723fd97c7f693548228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sikora?= Date: Thu, 20 Feb 2020 10:46:01 +0100 Subject: [PATCH 05/10] Change test name for upcoming #244 PR. --- .../{LiveDataUtilsTest.kt => LiveDataCombineLatestTest.kt} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename library/src/test/java/com/chuckerteam/chucker/internal/support/{LiveDataUtilsTest.kt => LiveDataCombineLatestTest.kt} (98%) diff --git a/library/src/test/java/com/chuckerteam/chucker/internal/support/LiveDataUtilsTest.kt b/library/src/test/java/com/chuckerteam/chucker/internal/support/LiveDataCombineLatestTest.kt similarity index 98% rename from library/src/test/java/com/chuckerteam/chucker/internal/support/LiveDataUtilsTest.kt rename to library/src/test/java/com/chuckerteam/chucker/internal/support/LiveDataCombineLatestTest.kt index 19ddd37c1..3c6df8248 100644 --- a/library/src/test/java/com/chuckerteam/chucker/internal/support/LiveDataUtilsTest.kt +++ b/library/src/test/java/com/chuckerteam/chucker/internal/support/LiveDataCombineLatestTest.kt @@ -9,7 +9,7 @@ import org.junit.Before import org.junit.Rule import org.junit.Test -class LiveDataUtilsTest { +class LiveDataCombineLatestTest { @get:Rule val instantExecutorRule = InstantTaskExecutorRule() private val liveDataA = MutableLiveData() From 2a75a10d7ddd2843655d656e330a88e03b14b979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sikora?= Date: Sat, 22 Feb 2020 19:18:03 +0100 Subject: [PATCH 06/10] Use LiveDataRecord for combineLatest tests. --- .../support/LiveDataCombineLatestTest.kt | 75 +++++++++---------- 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/library/src/test/java/com/chuckerteam/chucker/internal/support/LiveDataCombineLatestTest.kt b/library/src/test/java/com/chuckerteam/chucker/internal/support/LiveDataCombineLatestTest.kt index 3c6df8248..40494c528 100644 --- a/library/src/test/java/com/chuckerteam/chucker/internal/support/LiveDataCombineLatestTest.kt +++ b/library/src/test/java/com/chuckerteam/chucker/internal/support/LiveDataCombineLatestTest.kt @@ -2,74 +2,71 @@ package com.chuckerteam.chucker.internal.support import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.Observer +import com.chuckerteam.chucker.test import junit.framework.TestCase.assertEquals -import org.junit.After -import org.junit.Before import org.junit.Rule import org.junit.Test class LiveDataCombineLatestTest { @get:Rule val instantExecutorRule = InstantTaskExecutorRule() - private val liveDataA = MutableLiveData() - private val liveDataB = MutableLiveData() + private val inputA = MutableLiveData() + private val inputB = MutableLiveData() - private val combinedData = liveDataA.combineLatest(liveDataB) - private val results = mutableListOf>() - private val observer = Observer> { results += it } - - @Before - fun setUp() { - combinedData.observeForever(observer) - } - - @After - fun tearDown() { - combinedData.removeObserver(observer) - results.clear() - } + private val upstream = inputA.combineLatest(inputB) @Test fun firstEmptyValue_preventsDownstreamEmissions() { - liveDataB.value = 1 - liveDataB.value = 2 - liveDataB.value = 3 + upstream.test { + inputB.value = 1 + inputB.value = 2 + inputB.value = 3 - assertEquals(emptyList>(), results) + expectNoData() + } } @Test fun secondEmptyValue_preventsDownstreamEmissions() { - liveDataA.value = true - liveDataA.value = false + upstream.test { + inputA.value = true + inputA.value = false - assertEquals(emptyList>(), results) + expectNoData() + } } @Test fun bothEmittedValues_areCombinedDownstream() { - liveDataA.value = true - liveDataB.value = 1 + upstream.test { + inputA.value = true + inputB.value = 1 - assertEquals(listOf(true to 1), results) + assertEquals(true to 1, expectData()) + } } @Test fun lastFirstValue_isCombinedWithNewestSecondValues() { - liveDataA.value = true - liveDataB.value = 1 - liveDataB.value = 2 - - assertEquals(listOf(true to 1, true to 2), results) + upstream.test { + inputA.value = true + inputB.value = 1 + assertEquals(true to 1, expectData()) + + inputB.value = 2 + assertEquals(true to 2, expectData()) + } } @Test fun lastSecondValue_isCombinedWithNewestFirstValues() { - liveDataA.value = true - liveDataB.value = 1 - liveDataA.value = false - - assertEquals(listOf(true to 1, false to 1), results) + upstream.test { + inputA.value = true + inputB.value = 1 + assertEquals(true to 1, expectData()) + + inputA.value = false + assertEquals(false to 1, expectData()) + } } } From 2ca40e4ecaaf48f6cea5f8245cea389ae06aca30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sikora?= Date: Sat, 22 Feb 2020 19:24:58 +0100 Subject: [PATCH 07/10] Properly switch encoding and decoding URL. --- .../chucker/internal/ui/transaction/TransactionViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt index a25e528c2..9eae7d180 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt @@ -24,7 +24,7 @@ internal class TransactionViewModel( val transaction: LiveData = RepositoryProvider.transaction().getTransaction(transactionId) - fun switchUrlEncoding() = encodeUrl(encodeUrl.value!!) + fun switchUrlEncoding() = encodeUrl(!encodeUrl.value!!) fun encodeUrl(encode: Boolean) { mutableEncodeUrl.value = encode From a0199f88aac931a36b99d2f1111fc30b92386198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sikora?= Date: Sat, 22 Feb 2020 19:28:59 +0100 Subject: [PATCH 08/10] Show encoding icon only when it is viable. --- .../ui/transaction/TransactionOverviewFragment.kt | 5 ++++- .../internal/ui/transaction/TransactionViewModel.kt | 11 +++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionOverviewFragment.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionOverviewFragment.kt index 2f3d7e2f8..e28649a6f 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionOverviewFragment.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionOverviewFragment.kt @@ -58,7 +58,10 @@ internal class TransactionOverviewFragment : Fragment() { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { menu.findItem(R.id.save_body).isVisible = false - menu.findItem(R.id.encode_url).isVisible = true + viewModel.doesUrlRequireEncoding.observe( + viewLifecycleOwner, + Observer { menu.findItem(R.id.encode_url).isVisible = it } + ) super.onCreateOptionsMenu(menu, inflater) } diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt index 9eae7d180..934502f8f 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/transaction/TransactionViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.map import com.chuckerteam.chucker.internal.data.entity.HttpTransaction import com.chuckerteam.chucker.internal.data.repository.RepositoryProvider import com.chuckerteam.chucker.internal.support.combineLatest @@ -22,6 +23,16 @@ internal class TransactionViewModel( if (transaction != null) "${transaction.method} ${transaction.getFormattedPath(encode = encodeUrl)}" else "" } + val doesUrlRequireEncoding: LiveData = RepositoryProvider.transaction() + .getTransaction(transactionId) + .map { transaction -> + if (transaction == null) { + false + } else { + transaction.getFormattedPath(encode = true) != transaction.getFormattedPath(encode = false) + } + } + val transaction: LiveData = RepositoryProvider.transaction().getTransaction(transactionId) fun switchUrlEncoding() = encodeUrl(!encodeUrl.value!!) From 07aec08253eb580b8bd1f0fc19640c4c6a0b2df8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sikora?= Date: Sun, 23 Feb 2020 10:51:22 +0100 Subject: [PATCH 09/10] Add encoded URL samples to HttpBinClient. --- .../main/java/com/chuckerteam/chucker/sample/HttpBinClient.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sample/src/main/java/com/chuckerteam/chucker/sample/HttpBinClient.kt b/sample/src/main/java/com/chuckerteam/chucker/sample/HttpBinClient.kt index 9fe51b54b..ad790f8fb 100644 --- a/sample/src/main/java/com/chuckerteam/chucker/sample/HttpBinClient.kt +++ b/sample/src/main/java/com/chuckerteam/chucker/sample/HttpBinClient.kt @@ -86,6 +86,8 @@ class HttpBinClient( deny().enqueue(cb) cache("Mon").enqueue(cb) cache(30).enqueue(cb) + redirectTo("https://ascii.cl?parameter=%22Click+on+%27URL+Encode%27%21%22").enqueue(cb) + redirectTo("https://ascii.cl?parameter=\"Click on 'URL Encode'!\"").enqueue(cb) } } From ac20c437d53443456da49e625513f7cc98b730c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sikora?= Date: Sun, 23 Feb 2020 11:07:28 +0100 Subject: [PATCH 10/10] Avoid using 'this' scoping mechanism for URL. --- .../chucker/internal/data/entity/HttpTransaction.kt | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/data/entity/HttpTransaction.kt b/library/src/main/java/com/chuckerteam/chucker/internal/data/entity/HttpTransaction.kt index b41fc46ea..9c6d96897 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/data/entity/HttpTransaction.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/data/entity/HttpTransaction.kt @@ -207,9 +207,9 @@ internal class HttpTransaction( return responseBody?.let { formatBody(it, responseContentType) } ?: "" } - fun populateUrl(url: HttpUrl): HttpTransaction { - val formattedUrl = FormattedUrl.fromHttpUrl(url, encoded = false) - this.url = formattedUrl.url + fun populateUrl(httpUrl: HttpUrl): HttpTransaction { + val formattedUrl = FormattedUrl.fromHttpUrl(httpUrl, encoded = false) + url = formattedUrl.url host = formattedUrl.host path = formattedUrl.pathWithQuery scheme = formattedUrl.scheme @@ -217,15 +217,12 @@ internal class HttpTransaction( } fun getFormattedUrl(encode: Boolean): String { - val url = this.url ?: return "" - val httpUrl = HttpUrl.get(url) + val httpUrl = url?.let(HttpUrl::get) ?: return "" return FormattedUrl.fromHttpUrl(httpUrl, encode).url } fun getFormattedPath(encode: Boolean): String { - val url = this.url ?: return "" - - val httpUrl = HttpUrl.parse(url) ?: return "" + val httpUrl = url?.let(HttpUrl::get) ?: return "" return FormattedUrl.fromHttpUrl(httpUrl, encode).pathWithQuery }