From fd7893d3fc41c4fa956017618514aa9cd38c804a Mon Sep 17 00:00:00 2001 From: Volodymyr Buberenko Date: Sun, 9 Feb 2020 12:45:17 +0200 Subject: [PATCH] Release 3.1.2 (#231) * Remove scroll flags (#210) * Fix/gradle properties (#211) * Allow Gradle parallel build * Fix version name * Fix for curl command (#214) * Fix R8 minification crash on TransactionViewModel creation. (#219) * Big resources renaming (#216) * Fix clear action crash when application is dead (#222) * Fix for crash on Save transaction action (#221) * Show warning is transaction is null, fix crash in Save action * Uncomment sample transactions * Replace multiple returning with multiple throw due to detekt issue * Add message into IOException, update string for request being not ready * Fix for NPE in NotificationHelper (#223) * Add additional check fo transaction being not null before getting its notificationText * Extract transaction item from transactionBuffer * ViewModel refactoring (#220) * Update ViewModel dependency, refactor TransactionViewModel * Dependencies clean up * Switch to ViewModel on the main screen * Fix depleting bytes from the response. (#226) * Use HttpUrl instead of Uri for parsing URL data. * Do not read image sources directly from the response. * Simplify gzip logic. * Move gzip checks from IoUtils to OkHttpUtils. * Remove unused 'Response.hasBody()' extension. * Update library/src/main/java/com/chuckerteam/chucker/internal/support/OkHttpUtils.kt * Revert resource renaming (#227) * Revert renaming * Add changelogs for 3.1.2 (#230) --- CHANGELOG.md | 16 +++ README.md | 4 +- build.gradle | 31 +++--- gradle.properties | 6 +- library-no-op/src/main/AndroidManifest.xml | 15 --- library/build.gradle | 15 +-- library/src/main/AndroidManifest.xml | 15 --- .../chucker/api/ChuckerInterceptor.kt | 51 +++++----- .../internal/data/entity/HttpTransaction.kt | 14 ++- .../internal/support/ClearDatabaseService.kt | 2 + .../chucker/internal/support/FormatUtils.kt | 6 +- .../chucker/internal/support/IOUtils.kt | 6 -- .../chucker/internal/support/LiveDataUtils.kt | 13 --- .../internal/support/NotificationHelper.kt | 13 +-- .../chucker/internal/support/OkHttpUtils.kt | 45 +++++---- .../internal/ui/BaseChuckerActivity.kt | 20 +--- .../chucker/internal/ui/MainActivity.kt | 20 +--- .../chucker/internal/ui/MainViewModel.kt | 52 ++++++++++ .../internal/ui/error/ErrorListFragment.kt | 52 +++++----- .../ui/transaction/TransactionActivity.kt | 29 ++---- .../ui/transaction/TransactionAdapter.kt | 29 ++---- .../ui/transaction/TransactionListFragment.kt | 65 +++---------- .../TransactionOverviewFragment.kt | 21 +--- .../transaction/TransactionPayloadFragment.kt | 44 ++++----- .../ui/transaction/TransactionViewModel.kt | 30 +++--- ...e_24dp.xml => chucker_ic_delete_white.xml} | 0 ...xml => chucker_ic_error_notifications.xml} | 0 ...rey_24dp.xml => chucker_ic_https_grey.xml} | 0 ...ite_24dp.xml => chucker_ic_save_white.xml} | 0 ...e_24dp.xml => chucker_ic_search_white.xml} | 0 ...te_24dp.xml => chucker_ic_share_white.xml} | 0 ...> chucker_ic_transaction_notification.xml} | 0 .../res/layout/chucker_activity_error.xml | 17 +--- .../main/res/layout/chucker_activity_main.xml | 15 --- .../layout/chucker_activity_transaction.xml | 15 --- .../layout/chucker_fragment_error_list.xml | 16 +-- .../chucker_fragment_transaction_list.xml | 16 +-- .../chucker_fragment_transaction_overview.xml | 15 --- .../res/layout/chucker_list_item_error.xml | 15 --- .../layout/chucker_list_item_transaction.xml | 46 ++++----- .../chucker_transaction_item_body_line.xml | 2 +- .../chucker_transaction_item_headers.xml | 2 +- .../layout/chucker_transaction_item_image.xml | 2 +- library/src/main/res/menu/chucker_error.xml | 17 +--- .../src/main/res/menu/chucker_errors_list.xml | 17 +--- .../src/main/res/menu/chucker_transaction.xml | 21 +--- .../res/menu/chucker_transactions_list.xml | 19 +--- library/src/main/res/values-v21/styles.xml | 15 --- library/src/main/res/values/strings.xml | 18 +--- library/src/main/res/values/styles.xml | 15 --- .../java/com/chuckerteam/chucker/TestUtils.kt | 11 +++ .../chucker/api/ChuckerInterceptorTest.kt | 91 ++++++++++++++++++ .../chucker/internal/support/IOUtilsTest.kt | 10 -- .../internal/support/OkHttpUtilsTest.kt | 52 +++------- library/src/test/resources/sample_image.png | Bin 0 -> 1780 bytes sample/src/main/AndroidManifest.xml | 15 --- .../chucker/sample/HttpBinClient.kt | 2 +- .../chucker/sample/MainActivity.kt | 4 +- ...vity_main.xml => activity_main_sample.xml} | 16 +-- sample/src/main/res/values/colors.xml | 16 +-- sample/src/main/res/values/styles.xml | 6 +- 61 files changed, 440 insertions(+), 680 deletions(-) delete mode 100644 library/src/main/java/com/chuckerteam/chucker/internal/support/LiveDataUtils.kt create mode 100644 library/src/main/java/com/chuckerteam/chucker/internal/ui/MainViewModel.kt rename library/src/main/res/drawable/{chucker_ic_delete_white_24dp.xml => chucker_ic_delete_white.xml} (100%) rename library/src/main/res/drawable/{chucker_ic_error_notifications_24dp.xml => chucker_ic_error_notifications.xml} (100%) rename library/src/main/res/drawable/{chucker_ic_https_grey_24dp.xml => chucker_ic_https_grey.xml} (100%) rename library/src/main/res/drawable/{chucker_ic_save_white_24dp.xml => chucker_ic_save_white.xml} (100%) rename library/src/main/res/drawable/{chucker_ic_search_white_24dp.xml => chucker_ic_search_white.xml} (100%) rename library/src/main/res/drawable/{chucker_ic_share_white_24dp.xml => chucker_ic_share_white.xml} (100%) rename library/src/main/res/drawable/{chucker_ic_transaction_notification_24dp.xml => chucker_ic_transaction_notification.xml} (100%) create mode 100644 library/src/test/java/com/chuckerteam/chucker/TestUtils.kt create mode 100644 library/src/test/java/com/chuckerteam/chucker/api/ChuckerInterceptorTest.kt create mode 100644 library/src/test/resources/sample_image.png rename sample/src/main/res/layout/{activity_main.xml => activity_main_sample.xml} (71%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65a9d0eb3..55eb83a0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Change Log +## Version 3.1.2 *(2020-02-09)* + +This is hot-fix release to fix multiple issues introduced in `3.1.0`. + +### Summary of Changes +* All Chucker screens now have their own `ViewModel`. Due to this change user can now open the transaction in progress and the content will appear as soon as transaction finishes. No need for reopening transaction anymore. + +### Bugfixes + +* Fixed an [issue](https://github.com/ChuckerTeam/chucker/issues/225) introduced in 3.1.0 where image downloading fails if OkHttp was used for image loading in libraries like Glide, Picasso or Coil. +* Fixed an [issue](https://github.com/ChuckerTeam/chucker/pull/214) with invalid CURL command generation. +* Fixed an [issue](https://github.com/ChuckerTeam/chucker/issues/217) with crashes if ProGuard/R8 minification is applied to Chucker. +* Fixed an [issue](https://github.com/ChuckerTeam/chucker/pull/221) with crash when user taps Save in a transaction, which is still in progress. +* Fixed an [issue](https://github.com/ChuckerTeam/chucker/pull/222) with crash when user taps Clear from notification shade while the original app is already dead. +* Fixed an [issue](https://github.com/ChuckerTeam/chucker/pull/223) with possible NPEs. + ## Version 3.1.1 *(2020-01-25)* This is hot-fix release to fix issue introduced in `3.1.0`. diff --git a/README.md b/README.md index d4de8fd91..170e1e350 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,8 @@ repositories { ```groovy dependencies { - debugImplementation "com.github.ChuckerTeam.Chucker:library:3.1.1" - releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:3.1.1" + debugImplementation "com.github.ChuckerTeam.Chucker:library:3.1.2" + releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:3.1.2" } ``` diff --git a/build.gradle b/build.gradle index d946449ab..c23cc5415 100644 --- a/build.gradle +++ b/build.gradle @@ -1,24 +1,33 @@ buildscript { ext { + kotlinVersion = '1.3.61' androidGradleVersion = '3.5.3' - androidMavenGradleVersion = '2.1' + + // Google libraries appCompatVersion = '1.1.0' constraintLayoutVersion = '1.1.3' + materialComponentsVersion = '1.1.0-rc02' + roomVersion = '2.2.3' + lifecycleVersion = '2.2.0' + + // Publishing + androidMavenGradleVersion = '2.1' + + // Networking + gsonVersion = '2.8.6' + okhttp3Version = '3.12.6' + retrofitVersion = '2.6.4' + + // Debug and quality control detektVersion = '1.4.0' dokkaVersion = '0.10.0' - gradleBintrayVersion = '1.8.4' - gsonVersion = '2.8.6' - junitGradlePluignVersion = '1.3.1.1' - junitVersion = '5.4.2' - kotlinVersion = '1.3.61' ktLintVersion = '9.1.1' leakcanaryVersion = '2.1' - materialComponentsVersion = '1.1.0-rc02' + + // Testing + junitGradlePluignVersion = '1.3.1.1' + junitVersion = '5.4.2' mockkVersion = '1.9.3' - okhttp3Version = '3.12.6' - retrofitVersion = '2.6.4' - roomVersion = '2.2.3' - viewModelVersion = '2.1.0' } repositories { diff --git a/gradle.properties b/gradle.properties index aff0f6eec..b5e75b03b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,9 +16,9 @@ org.gradle.jvmargs=-Xmx1536m # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects org.gradle.parallel=true -VERSION_NAME=3.1.1 -# 3*100*100 + 1*100 + 1 => 30101 -VERSION_CODE=30101 +VERSION_NAME=3.1.2 +# 3*100*100 + 1*100 + 2 => 30102 +VERSION_CODE=30102 GROUP=com.github.chuckerteam.chucker POM_REPO_NAME=Chucker diff --git a/library-no-op/src/main/AndroidManifest.xml b/library-no-op/src/main/AndroidManifest.xml index 45626f79d..016e00a8d 100644 --- a/library-no-op/src/main/AndroidManifest.xml +++ b/library-no-op/src/main/AndroidManifest.xml @@ -1,19 +1,4 @@ - diff --git a/library/build.gradle b/library/build.gradle index 98a1ac3b7..67854cc8c 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -59,19 +59,22 @@ artifacts { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" - implementation "com.google.code.gson:gson:$gsonVersion" - implementation "com.squareup.okhttp3:okhttp:$okhttp3Version" implementation "com.google.android.material:material:$materialComponentsVersion" implementation "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion" + implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion" + implementation "androidx.room:room-runtime:$roomVersion" + kapt "androidx.room:room-compiler:$roomVersion" + + implementation "com.google.code.gson:gson:$gsonVersion" + implementation "com.squareup.okhttp3:okhttp:$okhttp3Version" + testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" testImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion" testImplementation "io.mockk:mockk:$mockkVersion" - - implementation "androidx.lifecycle:lifecycle-extensions:$viewModelVersion" - implementation "androidx.room:room-runtime:$roomVersion" - kapt "androidx.room:room-compiler:$roomVersion" + testImplementation "com.squareup.okhttp3:mockwebserver:$okhttp3Version" } apply from: rootProject.file('gradle/gradle-mvn-push.gradle') diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml index 0f59bd9e1..c37e83aa2 100644 --- a/library/src/main/AndroidManifest.xml +++ b/library/src/main/AndroidManifest.xml @@ -1,19 +1,4 @@ - diff --git a/library/src/main/java/com/chuckerteam/chucker/api/ChuckerInterceptor.kt b/library/src/main/java/com/chuckerteam/chucker/api/ChuckerInterceptor.kt index 01e7300b1..a466e43b3 100755 --- a/library/src/main/java/com/chuckerteam/chucker/api/ChuckerInterceptor.kt +++ b/library/src/main/java/com/chuckerteam/chucker/api/ChuckerInterceptor.kt @@ -3,7 +3,9 @@ package com.chuckerteam.chucker.api import android.content.Context import com.chuckerteam.chucker.internal.data.entity.HttpTransaction import com.chuckerteam.chucker.internal.support.IOUtils -import com.chuckerteam.chucker.internal.support.hasBody +import com.chuckerteam.chucker.internal.support.contentLenght +import com.chuckerteam.chucker.internal.support.contentType +import com.chuckerteam.chucker.internal.support.isGzipped import java.io.IOException import java.nio.charset.Charset import okhttp3.Headers @@ -60,10 +62,10 @@ class ChuckerInterceptor @JvmOverloads constructor( throw e } - processResponse(response, transaction) + val processedResponse = processResponse(response, transaction) collector.onResponseReceived(transaction) - return response + return processedResponse } /** @@ -76,7 +78,7 @@ class ChuckerInterceptor @JvmOverloads constructor( transaction.apply { setRequestHeaders(request.headers()) - populateUrl(request.url().toString()) + populateUrl(request.url()) isRequestBodyPlainText = encodingIsSupported requestDate = System.currentTimeMillis() @@ -86,7 +88,7 @@ class ChuckerInterceptor @JvmOverloads constructor( } if (requestBody != null && encodingIsSupported) { - val source = io.getNativeSource(Buffer(), io.bodyIsGzipped(request.headers().get(CONTENT_ENCODING))) + val source = io.getNativeSource(Buffer(), request.isGzipped) val buffer = source.buffer() requestBody.writeTo(buffer) var charset: Charset = UTF8 @@ -106,8 +108,7 @@ class ChuckerInterceptor @JvmOverloads constructor( /** * Processes a [Response] and populates corresponding fields of a [HttpTransaction]. */ - private fun processResponse(response: Response, transaction: HttpTransaction) { - val responseBody = response.body()!! + private fun processResponse(response: Response, transaction: HttpTransaction): Response { val responseEncodingIsSupported = io.bodyHasSupportedEncoding(response.headers().get(CONTENT_ENCODING)) transaction.apply { @@ -122,35 +123,35 @@ class ChuckerInterceptor @JvmOverloads constructor( responseCode = response.code() responseMessage = response.message() - responseContentType = responseBody.contentType()?.toString() - responseContentLength = responseBody.contentLength() + responseContentType = response.contentType + responseContentLength = response.contentLenght tookMs = (response.receivedResponseAtMillis() - response.sentRequestAtMillis()) } - if (response.hasBody() && responseEncodingIsSupported) { - processResponseBody(response, responseBody, transaction) + return if (responseEncodingIsSupported) { + processResponseBody(response, transaction) + } else { + response } } /** * Processes a [ResponseBody] and populates corresponding fields of a [HttpTransaction]. */ - private fun processResponseBody(response: Response, responseBody: ResponseBody, transaction: HttpTransaction) { + private fun processResponseBody(response: Response, transaction: HttpTransaction): Response { + val responseBody = response.body() ?: return response + val contentType = responseBody.contentType() - val charset: Charset = contentType?.charset(UTF8) ?: UTF8 + val charset = contentType?.charset(UTF8) ?: UTF8 val contentLength = responseBody.contentLength() - val source = responseBody.source() - source.request(Long.MAX_VALUE) // Buffer the entire body. - var buffer = source.buffer() - - if (io.bodyIsGzipped(response.headers()[CONTENT_ENCODING])) { - GzipSource(buffer.clone()).use { gzippedResponseBody -> - buffer = Buffer() - buffer.writeAll(gzippedResponseBody) - } + val responseSource = if (response.isGzipped) { + GzipSource(responseBody.source()) + } else { + responseBody.source() } + val buffer = Buffer().apply { responseSource.use { writeAll(it) } } if (io.isPlaintext(buffer)) { transaction.isResponseBodyPlainText = true @@ -164,9 +165,13 @@ class ChuckerInterceptor @JvmOverloads constructor( (contentType?.toString()?.contains(CONTENT_TYPE_IMAGE, ignoreCase = true) == true) if (isImageContentType && buffer.size() < MAX_BLOB_SIZE) { - transaction.responseImageData = buffer.readByteArray() + transaction.responseImageData = buffer.clone().readByteArray() } } + + return response.newBuilder() + .body(ResponseBody.create(contentType, contentLength, buffer)) + .build() } /** Overrides all headers from [headersToRedact] with `**` */ 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 1e0185b6a..b263af001 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 @@ -4,7 +4,6 @@ package com.chuckerteam.chucker.internal.data.entity import android.graphics.Bitmap import android.graphics.BitmapFactory -import android.net.Uri import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.Ignore @@ -13,8 +12,8 @@ import com.chuckerteam.chucker.internal.support.FormatUtils import com.chuckerteam.chucker.internal.support.JsonConverter import com.google.gson.reflect.TypeToken import java.util.Date -import kotlin.collections.ArrayList import okhttp3.Headers +import okhttp3.HttpUrl /** * Represent a full HTTP transaction (with Request and Response). Instances of this classes @@ -208,12 +207,11 @@ internal class HttpTransaction( return responseBody?.let { formatBody(it, responseContentType) } ?: "" } - fun populateUrl(url: String): HttpTransaction { - this.url = url - val uri = Uri.parse(url) - host = uri.host - path = ("${uri.path}${uri.query?.let { "?$it" } ?: ""}") - scheme = uri.scheme + fun populateUrl(url: HttpUrl): HttpTransaction { + this.url = url.toString() + host = url.host() + path = ("/${url.pathSegments().joinToString("/")}${url.query()?.let { "?$it" } ?: ""}") + scheme = url.scheme() return this } } diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/support/ClearDatabaseService.kt b/library/src/main/java/com/chuckerteam/chucker/internal/support/ClearDatabaseService.kt index 6f859206f..9da1a71d5 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/support/ClearDatabaseService.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/support/ClearDatabaseService.kt @@ -10,11 +10,13 @@ internal class ClearDatabaseService : IntentService(CLEAN_DATABASE_SERVICE_NAME) override fun onHandleIntent(intent: Intent?) { when (intent?.getSerializableExtra(EXTRA_ITEM_TO_CLEAR)) { is ClearAction.Transaction -> { + RepositoryProvider.initialize(applicationContext) RepositoryProvider.transaction().deleteAllTransactions() NotificationHelper.clearBuffer() NotificationHelper(this).dismissTransactionsNotification() } is ClearAction.Error -> { + RepositoryProvider.initialize(applicationContext) RepositoryProvider.throwable().deleteAllThrowables() NotificationHelper(this).dismissErrorsNotification() } 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 0eda66357..e18d39ee0 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 @@ -145,7 +145,7 @@ internal object FormatUtils { fun getShareCurlCommand(transaction: HttpTransaction): String { var compressed = false - var curlCmd = "curl -X $transaction.method" + var curlCmd = "curl -X ${transaction.method}" val headers = transaction.getParsedRequestHeaders() headers?.forEach { header -> @@ -154,13 +154,13 @@ internal object FormatUtils { ) { compressed = true } - curlCmd += " -H \"$header.name: $header.value\"" + curlCmd += " -H \"${header.name}: ${header.value}\"" } val requestBody = transaction.requestBody if (!requestBody.isNullOrEmpty()) { // try to keep to a single line and use a subshell to preserve any line breaks - curlCmd += " --data $'$requestBody.replace(\"\\n\", \"\\\\n\")'" + curlCmd += " --data $'${requestBody.replace("\n", "\\n")}'" } curlCmd += (if (compressed) " --compressed " else " ") + transaction.url return curlCmd diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/support/IOUtils.kt b/library/src/main/java/com/chuckerteam/chucker/internal/support/IOUtils.kt index b5422d3b8..6ec8cd1d1 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/support/IOUtils.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/support/IOUtils.kt @@ -66,10 +66,4 @@ internal class IOUtils(private val context: Context) { contentEncoding.isNullOrEmpty() || contentEncoding.equals("identity", ignoreCase = true) || contentEncoding.equals("gzip", ignoreCase = true) - - fun bodyIsGzipped(contentEncoding: String?) = CONTENT_ENCODING_GZIP.equals(contentEncoding, ignoreCase = true) - - private companion object { - const val CONTENT_ENCODING_GZIP = "gzip" - } } 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 deleted file mode 100644 index 693106258..000000000 --- a/library/src/main/java/com/chuckerteam/chucker/internal/support/LiveDataUtils.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.chuckerteam.chucker.internal.support - -import androidx.lifecycle.LiveData -import androidx.lifecycle.Observer - -internal fun LiveData.observeOnce(observer: Observer) { - observeForever(object : Observer { - override fun onChanged(t: T?) { - observer.onChanged(t) - removeObserver(this) - } - }) -} diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/support/NotificationHelper.kt b/library/src/main/java/com/chuckerteam/chucker/internal/support/NotificationHelper.kt index c2d875f59..7327c1f14 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/support/NotificationHelper.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/support/NotificationHelper.kt @@ -98,7 +98,7 @@ internal class NotificationHelper(val context: Context) { NotificationCompat.Builder(context, TRANSACTIONS_CHANNEL_ID) .setContentIntent(transactionsScreenIntent) .setLocalOnly(true) - .setSmallIcon(R.drawable.chucker_ic_transaction_notification_24dp) + .setSmallIcon(R.drawable.chucker_ic_transaction_notification) .setColor(ContextCompat.getColor(context, R.color.chucker_color_primary)) .setContentTitle(context.getString(R.string.chucker_http_notification_title)) .setAutoCancel(true) @@ -107,11 +107,12 @@ internal class NotificationHelper(val context: Context) { synchronized(transactionBuffer) { var count = 0 (transactionBuffer.size() - 1 downTo 0).forEach { i -> - if (count < BUFFER_SIZE) { + val bufferedTransaction = transactionBuffer.valueAt(i) + if ((bufferedTransaction != null) && count < BUFFER_SIZE) { if (count == 0) { - builder.setContentText(transactionBuffer.valueAt(i).notificationText) + builder.setContentText(bufferedTransaction.notificationText) } - inboxStyle.addLine(transactionBuffer.valueAt(i).notificationText) + inboxStyle.addLine(bufferedTransaction.notificationText) } count++ } @@ -132,7 +133,7 @@ internal class NotificationHelper(val context: Context) { NotificationCompat.Builder(context, ERRORS_CHANNEL_ID) .setContentIntent(errorsScreenIntent) .setLocalOnly(true) - .setSmallIcon(R.drawable.chucker_ic_error_notifications_24dp) + .setSmallIcon(R.drawable.chucker_ic_error_notifications) .setColor(ContextCompat.getColor(context, R.color.chucker_status_error)) .setContentTitle(throwable.clazz) .setAutoCancel(true) @@ -152,7 +153,7 @@ internal class NotificationHelper(val context: Context) { context, INTENT_REQUEST_CODE, deleteIntent, PendingIntent.FLAG_ONE_SHOT ) - return NotificationCompat.Action(R.drawable.chucker_ic_delete_white_24dp, clearTitle, intent) + return NotificationCompat.Action(R.drawable.chucker_ic_delete_white, clearTitle, intent) } fun dismissTransactionsNotification() { diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/support/OkHttpUtils.kt b/library/src/main/java/com/chuckerteam/chucker/internal/support/OkHttpUtils.kt index 2507b2bcc..f410d1d09 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/support/OkHttpUtils.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/support/OkHttpUtils.kt @@ -2,39 +2,38 @@ package com.chuckerteam.chucker.internal.support -import java.net.HttpURLConnection.HTTP_NOT_MODIFIED -import java.net.HttpURLConnection.HTTP_NO_CONTENT -import java.net.HttpURLConnection.HTTP_OK +import okhttp3.Headers +import okhttp3.Request import okhttp3.Response -const val HTTP_CONTINUE = 100 +internal val Response.contentLenght: Long + get() { + return this.header("Content-Length")?.toLongOrNull() ?: -1 + } -/** Returns true if the response must have a (possibly 0-length) body. See RFC 7231. */ -internal fun Response.hasBody(): Boolean { - // HEAD requests never yield a body regardless of the response headers. - if (this.request().method() == "HEAD") { - return false +internal val Response.isChunked: Boolean + get() { + return this.header("Transfer-Encoding").equals("chunked", ignoreCase = true) } - val responseCode = this.code() - if ((responseCode < HTTP_CONTINUE || responseCode >= HTTP_OK) && - responseCode != HTTP_NO_CONTENT && - responseCode != HTTP_NOT_MODIFIED - ) { - return true +internal val Response.contentType: String? + get() { + return this.header("Content-Type") } - // If the Content-Length or Transfer-Encoding headers disagree with the response code, the - // response is malformed. For best compatibility, we honor the headers. - return this.contentLenght != -1L || this.isChunked -} +/** Checks if the OkHttp response uses gzip encoding. */ +internal val Response.isGzipped: Boolean + get() { + return this.headers().containsGzip + } -internal val Response.contentLenght: Long +/** Checks if the OkHttp request uses gzip encoding. */ +internal val Request.isGzipped: Boolean get() { - return this.header("Content-Length")?.toLongOrNull() ?: -1 + return this.headers().containsGzip } -internal val Response.isChunked: Boolean +private val Headers.containsGzip: Boolean get() { - return this.header("Transfer-Encoding").equals("chunked", ignoreCase = true) + return this["Content-Encoding"].equals("gzip", ignoreCase = true) } diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/BaseChuckerActivity.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/BaseChuckerActivity.kt index 3bb7507c0..bc22e256d 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/BaseChuckerActivity.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/BaseChuckerActivity.kt @@ -1,21 +1,7 @@ -/* - * Copyright (C) 2017 Jeff Gilfelt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package com.chuckerteam.chucker.internal.ui import android.os.Bundle +import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import com.chuckerteam.chucker.internal.data.repository.RepositoryProvider @@ -40,4 +26,8 @@ internal abstract class BaseChuckerActivity : AppCompatActivity() { var isInForeground: Boolean = false private set } + + fun showToast(message: String) { + Toast.makeText(this, message, Toast.LENGTH_SHORT).show() + } } 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 c56ecd6cc..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 @@ -1,23 +1,9 @@ -/* - * Copyright (C) 2017 Jeff Gilfelt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package com.chuckerteam.chucker.internal.ui import android.content.Intent import android.os.Bundle import androidx.appcompat.widget.Toolbar +import androidx.lifecycle.ViewModelProvider import androidx.viewpager.widget.ViewPager import com.chuckerteam.chucker.R import com.chuckerteam.chucker.api.Chucker @@ -31,6 +17,8 @@ internal class MainActivity : BaseChuckerActivity(), TransactionAdapter.TransactionClickListListener, ErrorAdapter.ErrorClickListListener { + + private lateinit var viewModel: MainViewModel private lateinit var viewPager: ViewPager private val applicationName: CharSequence @@ -44,6 +32,8 @@ internal class MainActivity : setSupportActionBar(toolbar) toolbar.subtitle = applicationName + viewModel = ViewModelProvider(this).get(MainViewModel::class.java) + viewPager = findViewById(R.id.viewPager) viewPager.adapter = HomePageAdapter(this, supportFragmentManager) 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 new file mode 100644 index 000000000..9fc4cec89 --- /dev/null +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/MainViewModel.kt @@ -0,0 +1,52 @@ +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 com.chuckerteam.chucker.internal.data.entity.HttpTransactionTuple +import com.chuckerteam.chucker.internal.data.entity.RecordedThrowableTuple +import com.chuckerteam.chucker.internal.data.repository.RepositoryProvider +import com.chuckerteam.chucker.internal.support.NotificationHelper + +internal class MainViewModel : ViewModel() { + + private val currentFilter = MutableLiveData("") + + val transactions: LiveData> = Transformations.switchMap(currentFilter) { searchQuery -> + with(RepositoryProvider.transaction()) { + when { + searchQuery.isNullOrBlank() -> { + getSortedTransactionTuples() + } + TextUtils.isDigitsOnly(searchQuery) -> { + getFilteredTransactionTuples(searchQuery, "") + } + else -> { + getFilteredTransactionTuples("", searchQuery) + } + } + } + } + + val errors: LiveData> = + Transformations.map( + RepositoryProvider.throwable().getSortedThrowablesTuples() + ) { + it + } + + fun updateItemsFilter(searchQuery: String) { + currentFilter.value = searchQuery + } + + fun clearTransactions() { + RepositoryProvider.transaction().deleteAllTransactions() + NotificationHelper.clearBuffer() + } + + fun clearErrors() { + RepositoryProvider.throwable().deleteAllThrowables() + } +} diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/ui/error/ErrorListFragment.kt b/library/src/main/java/com/chuckerteam/chucker/internal/ui/error/ErrorListFragment.kt index f8051daad..a7fd06e42 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/ui/error/ErrorListFragment.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/ui/error/ErrorListFragment.kt @@ -13,14 +13,16 @@ import android.widget.TextView import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration.VERTICAL import androidx.recyclerview.widget.RecyclerView import com.chuckerteam.chucker.R -import com.chuckerteam.chucker.internal.data.repository.RepositoryProvider +import com.chuckerteam.chucker.internal.ui.MainViewModel internal class ErrorListFragment : Fragment() { + private lateinit var viewModel: MainViewModel private lateinit var adapter: ErrorAdapter private lateinit var listener: ErrorAdapter.ErrorClickListListener private lateinit var tutorialView: View @@ -28,6 +30,7 @@ internal class ErrorListFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) + viewModel = ViewModelProvider(requireActivity())[MainViewModel::class.java] } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -42,6 +45,21 @@ internal class ErrorListFragment : Fragment() { } } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + viewModel.errors.observe( + viewLifecycleOwner, + Observer { errors -> + adapter.setData(errors) + tutorialView.visibility = if (errors.isNullOrEmpty()) { + View.VISIBLE + } else { + View.GONE + } + } + ) + } + override fun onAttach(context: Context) { super.onAttach(context) @@ -49,20 +67,6 @@ internal class ErrorListFragment : Fragment() { "Context must implement the listener." } listener = context - - RepositoryProvider.throwable() - .getSortedThrowablesTuples() - .observe( - this, - Observer { tuples -> - adapter.setData(tuples) - if (tuples.isNullOrEmpty()) { - tutorialView.visibility = View.VISIBLE - } else { - tutorialView.visibility = View.GONE - } - } - ) } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { @@ -80,16 +84,14 @@ internal class ErrorListFragment : Fragment() { } private fun askForConfirmation() { - context?.let { - AlertDialog.Builder(it) - .setTitle(R.string.chucker_clear) - .setMessage(R.string.chucker_clear_error_confirmation) - .setPositiveButton(R.string.chucker_clear) { _, _ -> - RepositoryProvider.throwable().deleteAllThrowables() - } - .setNegativeButton(R.string.chucker_cancel, null) - .show() - } + AlertDialog.Builder(requireContext()) + .setTitle(R.string.chucker_clear) + .setMessage(R.string.chucker_clear_error_confirmation) + .setPositiveButton(R.string.chucker_clear) { _, _ -> + viewModel.clearErrors() + } + .setNegativeButton(R.string.chucker_cancel, null) + .show() } companion object { 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 2095c2334..a8cb99c45 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 @@ -1,18 +1,3 @@ -/* - * Copyright (C) 2017 Jeff Gilfelt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package com.chuckerteam.chucker.internal.ui.transaction import android.content.Context @@ -24,7 +9,7 @@ import android.widget.TextView import androidx.appcompat.widget.Toolbar import androidx.core.app.ShareCompat import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProviders +import androidx.lifecycle.ViewModelProvider import androidx.viewpager.widget.ViewPager import com.chuckerteam.chucker.R import com.chuckerteam.chucker.internal.support.FormatUtils.getShareCurlCommand @@ -45,10 +30,8 @@ internal class TransactionActivity : BaseChuckerActivity() { // Create the instance now, so it can be shared by the // various fragments in the view pager later. - viewModel = ViewModelProviders - .of(this, TransactionViewModelFactory(transactionId)) + viewModel = ViewModelProvider(this, TransactionViewModelFactory(transactionId)) .get(TransactionViewModel::class.java) - viewModel.loadTransaction() val toolbar = findViewById(R.id.toolbar) setSupportActionBar(toolbar) @@ -78,11 +61,15 @@ internal class TransactionActivity : BaseChuckerActivity() { override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { R.id.share_text -> { - share(getShareText(this, viewModel.transaction.value!!)) + viewModel.transaction.value?.let { + share(getShareText(this, it)) + } ?: showToast(getString(R.string.chucker_request_not_ready)) true } R.id.share_curl -> { - share(getShareCurlCommand(viewModel.transaction.value!!)) + viewModel.transaction.value?.let { + share(getShareCurlCommand(it)) + } ?: showToast(getString(R.string.chucker_request_not_ready)) true } else -> { 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 7061c220c..1abadefa0 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 @@ -1,18 +1,3 @@ -/* - * Copyright (C) 2017 Jeff Gilfelt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package com.chuckerteam.chucker.internal.ui.transaction import android.annotation.SuppressLint @@ -60,13 +45,13 @@ internal class TransactionAdapter internal constructor( } inner class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) { - private val code: TextView = view.findViewById(R.id.chucker_code) - private val path: TextView = view.findViewById(R.id.chucker_path) - private val host: TextView = view.findViewById(R.id.chucker_host) - private val start: TextView = view.findViewById(R.id.chucker_time_start) - private val duration: TextView = view.findViewById(R.id.chucker_duration) - private val size: TextView = view.findViewById(R.id.chucker_size) - private val ssl: ImageView = view.findViewById(R.id.chucker_ssl) + private val code: TextView = view.findViewById(R.id.code) + private val path: TextView = view.findViewById(R.id.path) + private val host: TextView = view.findViewById(R.id.host) + private val start: TextView = view.findViewById(R.id.time_start) + private val duration: TextView = view.findViewById(R.id.duration) + private val size: TextView = view.findViewById(R.id.size) + private val ssl: ImageView = view.findViewById(R.id.ssl) @SuppressLint("SetTextI18n") fun bind(transaction: HttpTransactionTuple) { 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 5209d779d..5d455cd47 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,23 +1,6 @@ -/* - * Copyright (C) 2017 Jeff Gilfelt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package com.chuckerteam.chucker.internal.ui.transaction -import android.content.Context import android.os.Bundle -import android.text.TextUtils import android.text.method.LinkMovementMethod import android.view.LayoutInflater import android.view.Menu @@ -29,29 +12,26 @@ import android.widget.TextView import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.SearchView import androidx.fragment.app.Fragment -import androidx.lifecycle.LiveData import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.RecyclerView import com.chuckerteam.chucker.R -import com.chuckerteam.chucker.internal.data.entity.HttpTransactionTuple -import com.chuckerteam.chucker.internal.data.repository.RepositoryProvider -import com.chuckerteam.chucker.internal.support.NotificationHelper +import com.chuckerteam.chucker.internal.ui.MainViewModel internal class TransactionListFragment : Fragment(), SearchView.OnQueryTextListener, - TransactionAdapter.TransactionClickListListener, - Observer> { + TransactionAdapter.TransactionClickListListener { - private var currentFilter = "" + private lateinit var viewModel: MainViewModel private lateinit var adapter: TransactionAdapter - private lateinit var dataSource: LiveData> private lateinit var tutorialView: View override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) + viewModel = ViewModelProvider(requireActivity())[MainViewModel::class.java] } override fun onCreateView( @@ -74,10 +54,15 @@ internal class TransactionListFragment : return view } - override fun onAttach(context: Context) { - super.onAttach(context) - dataSource = getDataSource(currentFilter) - dataSource.observe(this, this) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + viewModel.transactions.observe( + viewLifecycleOwner, + Observer { transactionTuples -> + adapter.setData(transactionTuples) + tutorialView.visibility = if (transactionTuples.isEmpty()) View.VISIBLE else View.GONE + } + ) } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { @@ -97,8 +82,7 @@ internal class TransactionListFragment : .setPositiveButton( R.string.chucker_clear ) { _, _ -> - RepositoryProvider.transaction().deleteAllTransactions() - NotificationHelper.clearBuffer() + viewModel.clearTransactions() } .setNegativeButton(R.string.chucker_cancel, null) .show() @@ -111,27 +95,10 @@ internal class TransactionListFragment : override fun onQueryTextSubmit(query: String): Boolean = true override fun onQueryTextChange(newText: String): Boolean { - currentFilter = newText - dataSource.removeObservers(this) - dataSource = getDataSource(currentFilter) - dataSource.observe(this, this) + viewModel.updateItemsFilter(newText) return true } - private fun getDataSource(currentFilter: String): LiveData> = when { - currentFilter.isEmpty() -> - RepositoryProvider.transaction().getSortedTransactionTuples() - TextUtils.isDigitsOnly(currentFilter) -> - RepositoryProvider.transaction().getFilteredTransactionTuples(currentFilter, "") - else -> - RepositoryProvider.transaction().getFilteredTransactionTuples("", currentFilter) - } - - override fun onChanged(tuples: List) { - adapter.setData(tuples) - tutorialView.visibility = if (tuples.isEmpty()) View.VISIBLE else View.GONE - } - override fun onTransactionClick(transactionId: Long, position: Int) = TransactionActivity.start(requireActivity(), transactionId) 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 60f2ce0d4..1fc6039f9 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 @@ -1,18 +1,3 @@ -/* - * Copyright (C) 2017 Jeff Gilfelt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package com.chuckerteam.chucker.internal.ui.transaction import android.os.Bundle @@ -24,7 +9,7 @@ import android.view.ViewGroup import android.widget.TextView import androidx.fragment.app.Fragment import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProviders +import androidx.lifecycle.ViewModelProvider import com.chuckerteam.chucker.R internal class TransactionOverviewFragment : Fragment() { @@ -46,7 +31,7 @@ internal class TransactionOverviewFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) - viewModel = ViewModelProviders.of(requireActivity())[TransactionViewModel::class.java] + viewModel = ViewModelProvider(requireActivity())[TransactionViewModel::class.java] } override fun onCreateView( @@ -80,7 +65,7 @@ internal class TransactionOverviewFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel.transaction.observe( - this, + viewLifecycleOwner, Observer { transaction -> url.text = transaction.url method.text = transaction.method 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 c556bbcba..93312550e 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 @@ -1,18 +1,3 @@ -/* - * Copyright (C) 2017 Jeff Gilfelt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package com.chuckerteam.chucker.internal.ui.transaction import android.annotation.SuppressLint @@ -38,7 +23,7 @@ import androidx.core.content.ContextCompat import androidx.core.text.HtmlCompat import androidx.fragment.app.Fragment import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProviders +import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.RecyclerView import com.chuckerteam.chucker.R import com.chuckerteam.chucker.internal.data.entity.HttpTransaction @@ -66,7 +51,7 @@ internal class TransactionPayloadFragment : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) type = arguments!!.getInt(ARG_TYPE) - viewModel = ViewModelProviders.of(requireActivity())[TransactionViewModel::class.java] + viewModel = ViewModelProvider(requireActivity())[TransactionViewModel::class.java] setHasOptionsMenu(true) } @@ -84,7 +69,7 @@ internal class TransactionPayloadFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel.transaction.observe( - this, + viewLifecycleOwner, Observer { transaction -> PayloadLoaderTask(this).execute(Pair(type, transaction)) } @@ -130,11 +115,11 @@ internal class TransactionPayloadFragment : else -> true } - private fun shouldShowSearchIcon(transaction: HttpTransaction?) = when { - (type == TYPE_REQUEST) -> { + private fun shouldShowSearchIcon(transaction: HttpTransaction?) = when (type) { + TYPE_REQUEST -> { (true == transaction?.isRequestBodyPlainText) && (0L != (transaction.requestContentLength)) } - (type == TYPE_RESPONSE) -> { + TYPE_RESPONSE -> { (true == transaction?.isResponseBodyPlainText) && (0L != (transaction.responseContentLength)) } else -> false @@ -256,7 +241,7 @@ internal class TransactionPayloadFragment : } } - class FileSaverTask(val fragment: TransactionPayloadFragment) : + class FileSaverTask(private val fragment: TransactionPayloadFragment) : AsyncTask, Unit, Boolean>() { @Suppress("NestedBlockDepth") @@ -266,15 +251,21 @@ internal class TransactionPayloadFragment : val context = fragment.context ?: return false context.contentResolver.openFileDescriptor(uri, "w")?.use { FileOutputStream(it.fileDescriptor).use { fos -> - when { - type == TYPE_REQUEST -> { + when (type) { + TYPE_REQUEST -> { transaction.requestBody?.byteInputStream()?.copyTo(fos) + ?: throw IOException(TRANSACTION_EXCEPTION) } - transaction.responseBody != null -> { + TYPE_RESPONSE -> { transaction.responseBody?.byteInputStream()?.copyTo(fos) + ?: throw IOException(TRANSACTION_EXCEPTION) } else -> { - fos.write(transaction.responseImageData) + if (transaction.responseImageData != null) { + fos.write(transaction.responseImageData) + } else { + throw IOException(TRANSACTION_EXCEPTION) + } } } } @@ -302,6 +293,7 @@ internal class TransactionPayloadFragment : companion object { private const val ARG_TYPE = "type" + private const val TRANSACTION_EXCEPTION = "Transaction not ready" const val TYPE_REQUEST = 0 const val TYPE_RESPONSE = 1 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 17fc22b68..732c6fd90 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,32 +1,28 @@ package com.chuckerteam.chucker.internal.ui.transaction -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.Observer +import androidx.lifecycle.LiveData +import androidx.lifecycle.Transformations 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.observeOnce -internal class TransactionViewModel(private val transactionId: Long) : ViewModel() { - internal val transactionTitle: MutableLiveData = MutableLiveData() - internal val transaction: MutableLiveData = MutableLiveData() +internal class TransactionViewModel(transactionId: Long) : ViewModel() { - fun loadTransaction() { - RepositoryProvider.transaction() - .getTransaction(transactionId) - .observeOnce( - Observer { - transactionTitle.value = if (it != null) "${it.method} ${it.path}" else "" - transaction.value = it - } - ) - } + val transactionTitle: LiveData = + Transformations.map(RepositoryProvider.transaction().getTransaction(transactionId)) { + if (it != null) "${it.method} ${it.path}" else "" + } + val transaction: LiveData = + Transformations.map(RepositoryProvider.transaction().getTransaction(transactionId)) { + it + } } internal class TransactionViewModelFactory(private val transactionId: Long = 0L) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { - return modelClass.getConstructor(Long::class.java).newInstance(transactionId) + require(modelClass == TransactionViewModel::class.java) { "Cannot create $modelClass" } + return TransactionViewModel(transactionId) as T } } diff --git a/library/src/main/res/drawable/chucker_ic_delete_white_24dp.xml b/library/src/main/res/drawable/chucker_ic_delete_white.xml similarity index 100% rename from library/src/main/res/drawable/chucker_ic_delete_white_24dp.xml rename to library/src/main/res/drawable/chucker_ic_delete_white.xml diff --git a/library/src/main/res/drawable/chucker_ic_error_notifications_24dp.xml b/library/src/main/res/drawable/chucker_ic_error_notifications.xml similarity index 100% rename from library/src/main/res/drawable/chucker_ic_error_notifications_24dp.xml rename to library/src/main/res/drawable/chucker_ic_error_notifications.xml diff --git a/library/src/main/res/drawable/chucker_ic_https_grey_24dp.xml b/library/src/main/res/drawable/chucker_ic_https_grey.xml similarity index 100% rename from library/src/main/res/drawable/chucker_ic_https_grey_24dp.xml rename to library/src/main/res/drawable/chucker_ic_https_grey.xml diff --git a/library/src/main/res/drawable/chucker_ic_save_white_24dp.xml b/library/src/main/res/drawable/chucker_ic_save_white.xml similarity index 100% rename from library/src/main/res/drawable/chucker_ic_save_white_24dp.xml rename to library/src/main/res/drawable/chucker_ic_save_white.xml diff --git a/library/src/main/res/drawable/chucker_ic_search_white_24dp.xml b/library/src/main/res/drawable/chucker_ic_search_white.xml similarity index 100% rename from library/src/main/res/drawable/chucker_ic_search_white_24dp.xml rename to library/src/main/res/drawable/chucker_ic_search_white.xml diff --git a/library/src/main/res/drawable/chucker_ic_share_white_24dp.xml b/library/src/main/res/drawable/chucker_ic_share_white.xml similarity index 100% rename from library/src/main/res/drawable/chucker_ic_share_white_24dp.xml rename to library/src/main/res/drawable/chucker_ic_share_white.xml diff --git a/library/src/main/res/drawable/chucker_ic_transaction_notification_24dp.xml b/library/src/main/res/drawable/chucker_ic_transaction_notification.xml similarity index 100% rename from library/src/main/res/drawable/chucker_ic_transaction_notification_24dp.xml rename to library/src/main/res/drawable/chucker_ic_transaction_notification.xml diff --git a/library/src/main/res/layout/chucker_activity_error.xml b/library/src/main/res/layout/chucker_activity_error.xml index 069240d26..097f746b8 100644 --- a/library/src/main/res/layout/chucker_activity_error.xml +++ b/library/src/main/res/layout/chucker_activity_error.xml @@ -1,19 +1,4 @@ - - - + + - - \ No newline at end of file diff --git a/library/src/main/res/layout/chucker_transaction_item_body_line.xml b/library/src/main/res/layout/chucker_transaction_item_body_line.xml index 6b71824f0..bb01550a5 100644 --- a/library/src/main/res/layout/chucker_transaction_item_body_line.xml +++ b/library/src/main/res/layout/chucker_transaction_item_body_line.xml @@ -6,5 +6,5 @@ android:textIsSelectable="true" android:minLines="1" android:gravity="left|start" - android:layout_marginHorizontal="16dp" + android:layout_marginHorizontal="@dimen/chucker_doub_grid" android:typeface="monospace" /> \ No newline at end of file diff --git a/library/src/main/res/layout/chucker_transaction_item_headers.xml b/library/src/main/res/layout/chucker_transaction_item_headers.xml index 05e8ffe36..acdae57dc 100644 --- a/library/src/main/res/layout/chucker_transaction_item_headers.xml +++ b/library/src/main/res/layout/chucker_transaction_item_headers.xml @@ -3,5 +3,5 @@ android:id="@+id/headers" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_margin="16dp" + android:layout_margin="@dimen/chucker_doub_grid" android:textIsSelectable="true" /> \ No newline at end of file diff --git a/library/src/main/res/layout/chucker_transaction_item_image.xml b/library/src/main/res/layout/chucker_transaction_item_image.xml index 2818940aa..e599b93fc 100644 --- a/library/src/main/res/layout/chucker_transaction_item_image.xml +++ b/library/src/main/res/layout/chucker_transaction_item_image.xml @@ -3,5 +3,5 @@ android:id="@+id/binary_data" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_margin="16dp" + android:layout_margin="@dimen/chucker_doub_grid" android:contentDescription="@string/chucker_binary_data" /> \ No newline at end of file diff --git a/library/src/main/res/menu/chucker_error.xml b/library/src/main/res/menu/chucker_error.xml index c4baa6f29..93f57299a 100644 --- a/library/src/main/res/menu/chucker_error.xml +++ b/library/src/main/res/menu/chucker_error.xml @@ -1,24 +1,9 @@ - \ No newline at end of file diff --git a/library/src/main/res/menu/chucker_errors_list.xml b/library/src/main/res/menu/chucker_errors_list.xml index 8e436d42f..67a5ed261 100644 --- a/library/src/main/res/menu/chucker_errors_list.xml +++ b/library/src/main/res/menu/chucker_errors_list.xml @@ -1,23 +1,8 @@ - \ 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 c6f22b70e..4d724d784 100644 --- a/library/src/main/res/menu/chucker_transaction.xml +++ b/library/src/main/res/menu/chucker_transaction.xml @@ -1,30 +1,15 @@ - @@ -39,7 +24,7 @@ - \ No newline at end of file diff --git a/library/src/main/res/values-v21/styles.xml b/library/src/main/res/values-v21/styles.xml index e5b1cb3d8..b6a4b78ec 100644 --- a/library/src/main/res/values-v21/styles.xml +++ b/library/src/main/res/values-v21/styles.xml @@ -1,19 +1,4 @@ -