From b3ca1686e3b3ad507f31d4757c30e88ca2d758b7 Mon Sep 17 00:00:00 2001 From: tom5079 Date: Fri, 21 Jan 2022 17:08:49 +0900 Subject: [PATCH] 5.2.19 Improved error report Lenient JSON decoding --- app/build.gradle | 2 +- app/release/output-metadata.json | 2 +- .../quaver/pupil/ExampleInstrumentedTest.kt | 2 +- app/src/main/java/xyz/quaver/pupil/Pupil.kt | 19 ++++--- .../pupil/adapters/GalleryBlockAdapter.kt | 2 +- .../java/xyz/quaver/pupil/hitomi/Utils.kt | 46 ---------------- .../java/xyz/quaver/pupil/hitomi/common.kt | 52 ++++++++++--------- .../java/xyz/quaver/pupil/hitomi/galleries.kt | 13 ++--- .../xyz/quaver/pupil/hitomi/galleryblock.kt | 18 ++----- .../java/xyz/quaver/pupil/hitomi/search.kt | 18 ++----- .../quaver/pupil/types/SendLogException.kt | 3 +- 11 files changed, 55 insertions(+), 122 deletions(-) delete mode 100644 app/src/main/java/xyz/quaver/pupil/hitomi/Utils.kt diff --git a/app/build.gradle b/app/build.gradle index d4b03bb6..1744915d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -38,7 +38,7 @@ android { minSdkVersion 16 targetSdkVersion 31 versionCode 69 - versionName "5.2.18" + versionName "5.2.19" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true } diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json index a5d4c0a7..534fea8c 100644 --- a/app/release/output-metadata.json +++ b/app/release/output-metadata.json @@ -12,7 +12,7 @@ "filters": [], "attributes": [], "versionCode": 69, - "versionName": "5.2.18", + "versionName": "5.2.19", "outputFile": "app-release.apk" } ], diff --git a/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt b/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt index eb10ab16..cbb8cde9 100644 --- a/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt +++ b/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt @@ -112,7 +112,7 @@ class ExampleInstrumentedTest { @Test fun test_getGalleryBlock() { runBlocking { - val block = getGalleryBlock(2013877) + val block = getGalleryBlock(2119310) Log.d("PUPILD", block.toString()) } diff --git a/app/src/main/java/xyz/quaver/pupil/Pupil.kt b/app/src/main/java/xyz/quaver/pupil/Pupil.kt index 70d91ab3..2c5e606e 100644 --- a/app/src/main/java/xyz/quaver/pupil/Pupil.kt +++ b/app/src/main/java/xyz/quaver/pupil/Pupil.kt @@ -27,6 +27,7 @@ import android.content.Context import android.content.Intent import android.net.Uri import android.os.Build +import android.util.Log import android.webkit.* import android.widget.Toast import androidx.appcompat.app.AppCompatDelegate @@ -45,6 +46,7 @@ import kotlinx.coroutines.flow.asSharedFlow import okhttp3.* import xyz.quaver.io.FileX import xyz.quaver.pupil.hitomi.evaluationContext +import xyz.quaver.pupil.types.JavascriptException import xyz.quaver.pupil.types.Tag import xyz.quaver.pupil.util.* import java.io.File @@ -95,9 +97,9 @@ fun reloadWebView() { runCatching { URL( if (BuildConfig.DEBUG) - "https://tom5079.github.io/Pupil/hitomi-dev.html" + "https://tom5079.github.io/PupilSources/hitomi-dev.html" else - "https://tom5079.github.io/Pupil/hitomi.html" + "https://tom5079.github.io/PupilSources/hitomi.html" ).readText() }.onFailure { webViewFailed = true @@ -123,9 +125,9 @@ fun reloadWhenFailedOrUpdate() = CoroutineScope(Dispatchers.Default).launch { runCatching { URL( if (BuildConfig.DEBUG) - "https://tom5079.github.io/Pupil/hitomi-dev.html.ver" + "https://tom5079.github.io/PupilSources/hitomi-dev.html.ver" else - "https://tom5079.github.io/Pupil/hitomi.html.ver" + "https://tom5079.github.io/PupilSources/hitomi.html.ver" ).readText() }.getOrNull().let { version -> (!version.isNullOrEmpty() && version != htmlVersion).also { @@ -188,12 +190,13 @@ fun initWebView(context: Context) { } } @JavascriptInterface - fun onError(uid: String, message: String) { + fun onError(uid: String, script: String, message: String, stack: String) { CoroutineScope(Dispatchers.Unconfined).launch { - _webViewFlow.emit(uid to null) + _webViewFlow.emit(uid to "") } - FirebaseCrashlytics.getInstance().log( - "onError: $message" + + FirebaseCrashlytics.getInstance().recordException( + JavascriptException("onError script: $script\nmessage: $message\nstack: $stack") ) } }, "Callback") diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt index f33d2f48..91cec1c5 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt @@ -186,7 +186,7 @@ class GalleryBlockAdapter(private val galleries: List) : RecyclerSwipeAdapt text = resources.getString(R.string.galleryblock_language, languages[galleryBlock.language]) visibility = when { - galleryBlock.language.isNotEmpty() -> View.VISIBLE + !galleryBlock.language.isNullOrEmpty() -> View.VISIBLE else -> View.GONE } } diff --git a/app/src/main/java/xyz/quaver/pupil/hitomi/Utils.kt b/app/src/main/java/xyz/quaver/pupil/hitomi/Utils.kt deleted file mode 100644 index 451106ec..00000000 --- a/app/src/main/java/xyz/quaver/pupil/hitomi/Utils.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2019 tom5079 - * - * 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 xyz.quaver - -import kotlinx.serialization.json.Json -import okhttp3.Request -import xyz.quaver.pupil.client -import java.io.IOException -import java.net.URL - -/** - * kotlinx.serialization.json.Json object for global use - * properties should not be changed - * - * @see [https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization.json/-json/index.html] - */ -val json = Json { - isLenient = true - ignoreUnknownKeys = true - allowSpecialFloatingPointValues = true - useArrayPolymorphism = true -} - -typealias HeaderSetter = (Request.Builder) -> Request.Builder -fun URL.readText(settings: HeaderSetter? = null): String { - val request = Request.Builder() - .url(this).let { - settings?.invoke(it) ?: it - }.build() - - return client.newCall(request).execute().also{ if (it.code() != 200) throw IOException() }.body()?.use { it.string() } ?: throw IOException() -} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/hitomi/common.kt b/app/src/main/java/xyz/quaver/pupil/hitomi/common.kt index a05520ae..18e131fa 100644 --- a/app/src/main/java/xyz/quaver/pupil/hitomi/common.kt +++ b/app/src/main/java/xyz/quaver/pupil/hitomi/common.kt @@ -16,11 +16,7 @@ package xyz.quaver.pupil.hitomi -import android.util.Log import android.webkit.WebView -import android.widget.Toast -import com.google.common.collect.ConcurrentHashMultiset -import com.google.firebase.crashlytics.FirebaseCrashlytics import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first @@ -29,10 +25,11 @@ import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -import xyz.quaver.json -import xyz.quaver.pupil.* +import xyz.quaver.pupil.webView +import xyz.quaver.pupil.webViewFailed +import xyz.quaver.pupil.webViewFlow +import xyz.quaver.pupil.webViewReady import java.util.* -import java.util.concurrent.ConcurrentHashMap import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine @@ -40,7 +37,20 @@ const val protocol = "https:" val evaluationContext = Dispatchers.Main + Job() -suspend fun WebView.evaluate(script: String): String = coroutineScope { +/** + * kotlinx.serialization.json.Json object for global use + * properties should not be changed + * + * @see [https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization.json/-json/index.html] + */ +val json = Json { + isLenient = true + ignoreUnknownKeys = true + allowSpecialFloatingPointValues = true + useArrayPolymorphism = true +} + +suspend inline fun WebView.evaluate(script: String): T = coroutineScope { var result: String? = null while (result == null) { @@ -60,14 +70,14 @@ suspend fun WebView.evaluate(script: String): String = coroutineScope { } } - result + json.decodeFromString(result) } @OptIn(ExperimentalCoroutinesApi::class) -suspend fun WebView.evaluatePromise( +suspend inline fun WebView.evaluatePromise( script: String, - then: String = ".then(result => Callback.onResult(%uid, JSON.stringify(result))).catch(err => Callback.onError(%uid, JSON.stringify(err)))" -): String = coroutineScope { + then: String = ".then(result => Callback.onResult(%uid, JSON.stringify(result))).catch(err => Callback.onError(%uid, String.raw`$script`, err.message, err.stack))" +): T = coroutineScope { var result: String? = null while (result == null) { @@ -95,27 +105,22 @@ suspend fun WebView.evaluatePromise( } } - result + json.decodeFromString(result) } @Suppress("EXPERIMENTAL_API_USAGE") -suspend fun getGalleryInfo(galleryID: Int): GalleryInfo { - val result = webView.evaluatePromise("get_gallery_info($galleryID)") - - return json.decodeFromString(result) -} +suspend fun getGalleryInfo(galleryID: Int): GalleryInfo = + webView.evaluatePromise("get_gallery_info($galleryID)") //common.js const val domain = "ltn.hitomi.la" -const val galleryblockdir = "galleryblock" -const val nozomiextension = ".nozomi" val String?.js: String get() = if (this == null) "null" else "'$this'" @OptIn(ExperimentalSerializationApi::class) -suspend fun urlFromUrlFromHash(galleryID: Int, image: GalleryFiles, dir: String? = null, ext: String? = null, base: String? = null): String { - val result = webView.evaluate( +suspend fun urlFromUrlFromHash(galleryID: Int, image: GalleryFiles, dir: String? = null, ext: String? = null, base: String? = null): String = + webView.evaluate( """ url_from_url_from_hash( ${galleryID.toString().js}, @@ -125,9 +130,6 @@ suspend fun urlFromUrlFromHash(galleryID: Int, image: GalleryFiles, dir: String? """.trimIndent() ) - return Json.decodeFromString(result) -} - suspend fun imageUrlFromImage(galleryID: Int, image: GalleryFiles, noWebp: Boolean) : String { return when { noWebp -> diff --git a/app/src/main/java/xyz/quaver/pupil/hitomi/galleries.kt b/app/src/main/java/xyz/quaver/pupil/hitomi/galleries.kt index f1602834..4b60931f 100644 --- a/app/src/main/java/xyz/quaver/pupil/hitomi/galleries.kt +++ b/app/src/main/java/xyz/quaver/pupil/hitomi/galleries.kt @@ -19,17 +19,12 @@ package xyz.quaver.pupil.hitomi import kotlinx.serialization.Serializable import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json -import org.jsoup.Jsoup import xyz.quaver.pupil.webView -import xyz.quaver.readText -import java.net.URL -import java.net.URLDecoder @Serializable data class Gallery( val related: List, - val langList: Map - , + val langList: Map, val cover: String, val title: String, val artists: List, @@ -41,7 +36,5 @@ data class Gallery( val tags: List, val thumbnails: List ) -suspend fun getGallery(galleryID: Int) : Gallery { - val result = webView.evaluatePromise("get_gallery($galleryID)") - return Json.decodeFromString(result) -} \ No newline at end of file +suspend fun getGallery(galleryID: Int) : Gallery = + webView.evaluatePromise("get_gallery($galleryID)") \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/hitomi/galleryblock.kt b/app/src/main/java/xyz/quaver/pupil/hitomi/galleryblock.kt index 9b598314..e49d6975 100644 --- a/app/src/main/java/xyz/quaver/pupil/hitomi/galleryblock.kt +++ b/app/src/main/java/xyz/quaver/pupil/hitomi/galleryblock.kt @@ -17,17 +17,7 @@ package xyz.quaver.pupil.hitomi import kotlinx.serialization.Serializable -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.json.Json -import org.jsoup.Jsoup import xyz.quaver.pupil.webView -import xyz.quaver.readText -import java.net.URL -import java.net.URLDecoder -import java.nio.ByteBuffer -import java.nio.ByteOrder -import java.util.* -import javax.net.ssl.HttpsURLConnection @Serializable data class GalleryBlock( @@ -38,11 +28,9 @@ data class GalleryBlock( val artists: List, val series: List, val type: String, - val language: String, + val language: String?, val relatedTags: List ) -suspend fun getGalleryBlock(galleryID: Int) : GalleryBlock { - val result = webView.evaluatePromise("get_gallery_block($galleryID)") - return Json.decodeFromString(result) -} \ No newline at end of file +suspend fun getGalleryBlock(galleryID: Int) : GalleryBlock = + webView.evaluatePromise("get_gallery_block($galleryID)") \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/hitomi/search.kt b/app/src/main/java/xyz/quaver/pupil/hitomi/search.kt index 9f1326b4..649db5da 100644 --- a/app/src/main/java/xyz/quaver/pupil/hitomi/search.kt +++ b/app/src/main/java/xyz/quaver/pupil/hitomi/search.kt @@ -27,18 +27,15 @@ import xyz.quaver.pupil.webView const val extension = ".html" @OptIn(ExperimentalSerializationApi::class) -suspend fun getGalleryIDsForQuery(query: String) : Set { - val result = webView.evaluatePromise("get_galleryids_for_query('$query')") - - return Json.decodeFromString(result) -} +suspend fun getGalleryIDsForQuery(query: String) : Set = + webView.evaluatePromise("get_galleryids_for_query('$query')") @Serializable data class Suggestion(val s: String, val t: Int, val u: String, val n: String) @OptIn(ExperimentalSerializationApi::class) -suspend fun getSuggestionsForQuery(query: String) : List { - val result = webView.evaluatePromise( +suspend fun getSuggestionsForQuery(query: String) : List = + webView.evaluatePromise( "get_suggestions_for_query('$query', ++search_serial)", then = """ .then(r => { @@ -52,14 +49,9 @@ suspend fun getSuggestionsForQuery(query: String) : List { """.trimIndent() ) - return Json.decodeFromString(result) ?: return emptyList() -} - @OptIn(ExperimentalSerializationApi::class) suspend fun getGalleryIDsFromNozomi(area: String?, tag: String, language: String) : Set { val jsArea = if (area == null) "null" else "'$area'" - val json = webView.evaluatePromise("""get_galleryids_from_nozomi($jsArea, '$tag', '$language')""") - - return Json.decodeFromString(json) + return webView.evaluatePromise("""get_galleryids_from_nozomi($jsArea, '$tag', '$language')""") } \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/types/SendLogException.kt b/app/src/main/java/xyz/quaver/pupil/types/SendLogException.kt index f943953f..13e8b9ad 100644 --- a/app/src/main/java/xyz/quaver/pupil/types/SendLogException.kt +++ b/app/src/main/java/xyz/quaver/pupil/types/SendLogException.kt @@ -18,4 +18,5 @@ package xyz.quaver.pupil.types -class SendLogException : Exception() \ No newline at end of file +class SendLogException : Exception() +class JavascriptException(message: String?) : Exception(message) \ No newline at end of file