From ac834f3be52f232d3c0499f4edeab08339741749 Mon Sep 17 00:00:00 2001 From: RadiationX Date: Wed, 28 Dec 2022 17:11:53 +0500 Subject: [PATCH 1/3] fix: comments crash --- .../ui/fragments/comments/VkCommentsFragment.kt | 12 ++++++------ .../anilibria/ui/fragments/page/PageFragment.kt | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app-mobile/src/main/java/ru/radiationx/anilibria/ui/fragments/comments/VkCommentsFragment.kt b/app-mobile/src/main/java/ru/radiationx/anilibria/ui/fragments/comments/VkCommentsFragment.kt index 223326381..72f6fba62 100644 --- a/app-mobile/src/main/java/ru/radiationx/anilibria/ui/fragments/comments/VkCommentsFragment.kt +++ b/app-mobile/src/main/java/ru/radiationx/anilibria/ui/fragments/comments/VkCommentsFragment.kt @@ -122,8 +122,8 @@ class VkCommentsFragment : BaseDimensionsFragment(R.layout.fragment_vk_comments) override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - binding.webView.let { - outState.putInt(WEB_VIEW_SCROLL_Y, it.scrollY) + if (view != null) { + outState.putInt(WEB_VIEW_SCROLL_Y, binding.webView.scrollY) } } @@ -213,7 +213,7 @@ class VkCommentsFragment : BaseDimensionsFragment(R.layout.fragment_vk_comments) view: WebView?, isDialog: Boolean, isUserGesture: Boolean, - resultMsg: Message + resultMsg: Message, ): Boolean { val newWebView = WebView(requireContext()) AlertDialog.Builder(requireContext()) @@ -336,7 +336,7 @@ class VkCommentsFragment : BaseDimensionsFragment(R.layout.fragment_vk_comments) override fun onReceivedSslError( view: WebView?, handler: SslErrorHandler?, - error: SslError? + error: SslError?, ) { super.onReceivedSslError(view, handler, error) viewModel.onPageCommitError(error.toException()) @@ -345,7 +345,7 @@ class VkCommentsFragment : BaseDimensionsFragment(R.layout.fragment_vk_comments) override fun onReceivedHttpError( view: WebView?, request: WebResourceRequest?, - errorResponse: WebResourceResponse? + errorResponse: WebResourceResponse?, ) { super.onReceivedHttpError(view, request, errorResponse) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && view?.url == request?.url?.toString()) { @@ -356,7 +356,7 @@ class VkCommentsFragment : BaseDimensionsFragment(R.layout.fragment_vk_comments) override fun onReceivedError( view: WebView?, request: WebResourceRequest?, - error: WebResourceError? + error: WebResourceError?, ) { super.onReceivedError(view, request, error) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && view?.url == request?.url?.toString()) { diff --git a/app-mobile/src/main/java/ru/radiationx/anilibria/ui/fragments/page/PageFragment.kt b/app-mobile/src/main/java/ru/radiationx/anilibria/ui/fragments/page/PageFragment.kt index c637bc2dd..da9f8c076 100644 --- a/app-mobile/src/main/java/ru/radiationx/anilibria/ui/fragments/page/PageFragment.kt +++ b/app-mobile/src/main/java/ru/radiationx/anilibria/ui/fragments/page/PageFragment.kt @@ -114,7 +114,7 @@ class PageFragment : BaseToolbarFragment(R.layout.fragme override fun onReceivedSslError( view: WebView?, handler: SslErrorHandler?, - error: SslError? + error: SslError?, ) { super.onReceivedSslError(view, handler, error) pageAnalytics.error(error.toException()) @@ -123,7 +123,7 @@ class PageFragment : BaseToolbarFragment(R.layout.fragme override fun onReceivedHttpError( view: WebView?, request: WebResourceRequest?, - errorResponse: WebResourceResponse? + errorResponse: WebResourceResponse?, ) { super.onReceivedHttpError(view, request, errorResponse) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && view?.url == request?.url?.toString()) { @@ -134,7 +134,7 @@ class PageFragment : BaseToolbarFragment(R.layout.fragme override fun onReceivedError( view: WebView?, request: WebResourceRequest?, - error: WebResourceError? + error: WebResourceError?, ) { super.onReceivedError(view, request, error) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && view?.url == request?.url?.toString()) { @@ -181,8 +181,8 @@ class PageFragment : BaseToolbarFragment(R.layout.fragme override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - binding.webView.let { - outState.putInt(WEB_VIEW_SCROLL_Y, it.scrollY) + if (view != null) { + outState.putInt(WEB_VIEW_SCROLL_Y, binding.webView.scrollY) } } From 718362a46703eacf6eb48e44998dedd7a83dc0d5 Mon Sep 17 00:00:00 2001 From: RadiationX Date: Wed, 28 Dec 2022 21:17:55 +0500 Subject: [PATCH 2/3] fix: comments strange crash --- .../fragments/comments/VkCommentsFragment.kt | 226 +++--------------- .../comments/webview/VkWebChromeClient.kt | 48 ++++ .../comments/webview/VkWebViewClient.kt | 156 ++++++++++++ 3 files changed, 236 insertions(+), 194 deletions(-) create mode 100644 app-mobile/src/main/java/ru/radiationx/anilibria/ui/fragments/comments/webview/VkWebChromeClient.kt create mode 100644 app-mobile/src/main/java/ru/radiationx/anilibria/ui/fragments/comments/webview/VkWebViewClient.kt diff --git a/app-mobile/src/main/java/ru/radiationx/anilibria/ui/fragments/comments/VkCommentsFragment.kt b/app-mobile/src/main/java/ru/radiationx/anilibria/ui/fragments/comments/VkCommentsFragment.kt index 72f6fba62..535f4eca8 100644 --- a/app-mobile/src/main/java/ru/radiationx/anilibria/ui/fragments/comments/VkCommentsFragment.kt +++ b/app-mobile/src/main/java/ru/radiationx/anilibria/ui/fragments/comments/VkCommentsFragment.kt @@ -1,14 +1,13 @@ package ru.radiationx.anilibria.ui.fragments.comments -import android.app.AlertDialog -import android.graphics.Bitmap -import android.net.http.SslError import android.os.Build import android.os.Bundle -import android.os.Message import android.util.Log import android.view.View -import android.webkit.* +import android.webkit.CookieManager +import android.webkit.JavascriptInterface +import android.webkit.WebChromeClient +import android.webkit.WebViewClient import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope import by.kirich1409.viewbindingdelegate.viewBinding @@ -16,19 +15,19 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.runBlocking import ru.radiationx.anilibria.R import ru.radiationx.anilibria.apptheme.AppThemeController import ru.radiationx.anilibria.databinding.FragmentVkCommentsBinding import ru.radiationx.anilibria.extension.generateWithTheme import ru.radiationx.anilibria.extension.getWebStyleType -import ru.radiationx.anilibria.extension.isDark import ru.radiationx.anilibria.model.loading.hasAnyLoading import ru.radiationx.anilibria.ui.common.Templates import ru.radiationx.anilibria.ui.common.webpage.WebPageStateWebViewClient import ru.radiationx.anilibria.ui.common.webpage.WebPageViewState import ru.radiationx.anilibria.ui.common.webpage.compositeWebViewClientOf import ru.radiationx.anilibria.ui.fragments.BaseDimensionsFragment +import ru.radiationx.anilibria.ui.fragments.comments.webview.VkWebChromeClient +import ru.radiationx.anilibria.ui.fragments.comments.webview.VkWebViewClient import ru.radiationx.anilibria.ui.widgets.ExtendedWebView import ru.radiationx.data.MainClient import ru.radiationx.data.datasource.remote.IClient @@ -36,11 +35,7 @@ import ru.radiationx.quill.get import ru.radiationx.quill.inject import ru.radiationx.quill.viewModel import ru.radiationx.shared.ktx.android.toBase64 -import ru.radiationx.shared.ktx.android.toException import ru.radiationx.shared_app.common.SystemUtils -import timber.log.Timber -import java.io.ByteArrayInputStream -import java.nio.charset.StandardCharsets class VkCommentsFragment : BaseDimensionsFragment(R.layout.fragment_vk_comments) { @@ -83,15 +78,21 @@ class VkCommentsFragment : BaseDimensionsFragment(R.layout.fragment_vk_comments) webViewScrollPos = it.getInt(WEB_VIEW_SCROLL_Y, 0) } - binding.webView.setJsLifeCycleListener(jsLifeCycleListener) + binding.webView.setJsLifeCycleListener(jsLifeCycleListener(binding.webView)) binding.webView.addJavascriptInterface(this, "KEK") binding.webView.settings.apply { this.databaseEnabled = true } - binding.webView.webViewClient = composite - binding.webView.webChromeClient = vkWebChromeClient + binding.webView.webViewClient = composite( + viewModel = viewModel, + systemUtils = systemUtils, + networkClient = get(MainClient::class), + commentsCss = get(), + appThemeController = appThemeController + ) + binding.webView.webChromeClient = VkWebChromeClient(viewModel) val cookieManager = CookieManager.getInstance() @@ -139,10 +140,10 @@ class VkCommentsFragment : BaseDimensionsFragment(R.layout.fragment_vk_comments) override fun onDestroyView() { super.onDestroyView() - binding.webView.endWork() binding.webView.setJsLifeCycleListener(null) binding.webView.webViewClient = WebViewClient() binding.webView.webChromeClient = WebChromeClient() + binding.webView.endWork() } private fun showState(state: VkCommentsScreenState) { @@ -175,13 +176,15 @@ class VkCommentsFragment : BaseDimensionsFragment(R.layout.fragment_vk_comments) binding.webView.evalJs("ViewModel.setText('content','${comments.script.toBase64()}');") } - private val jsLifeCycleListener = object : ExtendedWebView.JsLifeCycleListener { + private fun jsLifeCycleListener( + webView: ExtendedWebView, + ) = object : ExtendedWebView.JsLifeCycleListener { override fun onDomContentComplete(actions: ArrayList?) { } override fun onPageComplete(actions: ArrayList?) { - binding.webView.syncWithJs { - binding.webView.scrollTo(0, webViewScrollPos) + webView.syncWithJs { + webView.scrollTo(0, webViewScrollPos) } } } @@ -191,182 +194,17 @@ class VkCommentsFragment : BaseDimensionsFragment(R.layout.fragment_vk_comments) Log.d("VkCommentsFragment.log", string) } - private val vkWebChromeClient = object : WebChromeClient() { - - private val jsErrorRegex = Regex("Uncaught (?:\\w+)Error:") - private val sourceRegex = Regex("https?://vk\\.com/") - - override fun onConsoleMessage(consoleMessage: ConsoleMessage?): Boolean { - return super.onConsoleMessage(consoleMessage) - } - - override fun onConsoleMessage(message: String?, lineNumber: Int, sourceID: String?) { - super.onConsoleMessage(message, lineNumber, sourceID) - val hasJsError = jsErrorRegex.containsMatchIn(message.orEmpty()) - val isVkSource = sourceRegex.containsMatchIn(sourceID.orEmpty()) - if (hasJsError && isVkSource) { - viewModel.notifyNewJsError() - } - } - - override fun onCreateWindow( - view: WebView?, - isDialog: Boolean, - isUserGesture: Boolean, - resultMsg: Message, - ): Boolean { - val newWebView = WebView(requireContext()) - AlertDialog.Builder(requireContext()) - .setView(newWebView) - .show() - val transport = resultMsg.obj as WebView.WebViewTransport - transport.webView = newWebView - resultMsg.sendToTarget() - return true - } - } - - private val stateClient = WebPageStateWebViewClient { - viewModel.onNewPageState(it) - } - - private val vkWebViewClient = object : WebViewClient() { - - var loadingFinished = true - var redirect = false - var authCheckIntercepted = false - - private val authRequestRegex = Regex("oauth\\.vk\\.com\\/authorize\\?|vk\\.com\\/login\\?") - private val authCheckRegex = Regex("vk\\.com\\/login\\?act=authcheck") - private val commentsRegex = Regex("widget_comments(?:\\.\\w+?)?\\.css") - - @Suppress("DEPRECATION", "OverridingDeprecatedMember") - override fun shouldInterceptRequest(view: WebView?, url: String?): WebResourceResponse? { - return tryInterceptAuthCheck(view, url) - ?: tryInterceptComments(view, url) - ?: super.shouldInterceptRequest(view, url) - } - - private fun tryInterceptAuthCheck(view: WebView?, url: String?): WebResourceResponse? { - val needAuth = authCheckRegex.containsMatchIn(url.orEmpty()) - if (needAuth && !authCheckIntercepted) { - authCheckIntercepted = true - val mobileUrl = if (!url.orEmpty().contains("/m.vk.com/")) { - url.orEmpty().replace("vk.com", "/m.vk.com/") - } else { - url.orEmpty() - } - viewModel.authRequest(mobileUrl) - } - return null - } - - private fun tryInterceptComments(view: WebView?, url: String?): WebResourceResponse? { - val needIntercept = commentsRegex.containsMatchIn(url.orEmpty()) - return if (needIntercept) { - val networkClient = get(MainClient::class) - val cssSrc = try { - runBlocking { networkClient.get(url.orEmpty(), emptyMap()) } - } catch (ex: Throwable) { - Timber.e(ex) - return WebResourceResponse( - "text/css", - "utf-8", - ByteArrayInputStream( - ex.message.orEmpty().toByteArray(StandardCharsets.UTF_8) - ) - ) - } - var newCss = cssSrc - - val commentsCss = get() - val fixCss = if (appThemeController.getTheme().isDark()) { - commentsCss.dark - } else { - commentsCss.light - } - - newCss += fixCss - - val newData = ByteArrayInputStream(newCss.toByteArray(StandardCharsets.UTF_8)) - WebResourceResponse("text/css", "utf-8", newData) - } else { - null - } - } - - @Suppress("OverridingDeprecatedMember") - override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean { - val cookies = CookieManager.getInstance().getCookie(url) - if (!loadingFinished) { - redirect = true - } - - loadingFinished = false - - if (url.orEmpty().contains(authRequestRegex)) { - viewModel.authRequest(url.orEmpty()) - return true - } - systemUtils.externalLink(url.orEmpty()) - return true - } - - override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { - super.onPageStarted(view, url, favicon) - authCheckIntercepted = false - loadingFinished = false - } - - override fun onPageFinished(view: WebView?, url: String?) { - super.onPageFinished(view, url) - - if (!redirect) { - loadingFinished = true - } - - if (loadingFinished && !redirect) { - //progressBar.visibility = View.GONE - viewModel.onPageLoaded() - } else { - redirect = false - } - } - - override fun onReceivedSslError( - view: WebView?, - handler: SslErrorHandler?, - error: SslError?, - ) { - super.onReceivedSslError(view, handler, error) - viewModel.onPageCommitError(error.toException()) - } - - override fun onReceivedHttpError( - view: WebView?, - request: WebResourceRequest?, - errorResponse: WebResourceResponse?, - ) { - super.onReceivedHttpError(view, request, errorResponse) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && view?.url == request?.url?.toString()) { - viewModel.onPageCommitError(errorResponse.toException(request)) - } - } - - override fun onReceivedError( - view: WebView?, - request: WebResourceRequest?, - error: WebResourceError?, - ) { - super.onReceivedError(view, request, error) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && view?.url == request?.url?.toString()) { - viewModel.onPageCommitError(error.toException(request)) - } - } - } - private val composite = compositeWebViewClientOf( - stateClient, - vkWebViewClient + private fun composite( + viewModel: VkCommentsViewModel, + systemUtils: SystemUtils, + networkClient: IClient, + commentsCss: VkCommentsCss, + appThemeController: AppThemeController, + ) = compositeWebViewClientOf( + WebPageStateWebViewClient { + viewModel.onNewPageState(it) + }, + VkWebViewClient(viewModel, systemUtils, networkClient, commentsCss, appThemeController) ) } \ No newline at end of file diff --git a/app-mobile/src/main/java/ru/radiationx/anilibria/ui/fragments/comments/webview/VkWebChromeClient.kt b/app-mobile/src/main/java/ru/radiationx/anilibria/ui/fragments/comments/webview/VkWebChromeClient.kt new file mode 100644 index 000000000..4f96ab175 --- /dev/null +++ b/app-mobile/src/main/java/ru/radiationx/anilibria/ui/fragments/comments/webview/VkWebChromeClient.kt @@ -0,0 +1,48 @@ +package ru.radiationx.anilibria.ui.fragments.comments.webview + +import android.app.AlertDialog +import android.os.Message +import android.webkit.ConsoleMessage +import android.webkit.WebChromeClient +import android.webkit.WebView +import ru.radiationx.anilibria.ui.fragments.comments.VkCommentsViewModel + +class VkWebChromeClient( + private val viewModel: VkCommentsViewModel, +) : WebChromeClient() { + + private val jsErrorRegex = Regex("Uncaught (?:\\w+)Error:") + private val sourceRegex = Regex("https?://vk\\.com/") + + override fun onConsoleMessage(consoleMessage: ConsoleMessage?): Boolean { + return super.onConsoleMessage(consoleMessage) + } + + override fun onConsoleMessage(message: String?, lineNumber: Int, sourceID: String?) { + super.onConsoleMessage(message, lineNumber, sourceID) + val hasJsError = jsErrorRegex.containsMatchIn(message.orEmpty()) + val isVkSource = sourceRegex.containsMatchIn(sourceID.orEmpty()) + if (hasJsError && isVkSource) { + viewModel.notifyNewJsError() + } + } + + override fun onCreateWindow( + view: WebView?, + isDialog: Boolean, + isUserGesture: Boolean, + resultMsg: Message, + ): Boolean { + if (view == null) { + return false + } + val newWebView = WebView(view.context) + AlertDialog.Builder(view.context) + .setView(newWebView) + .show() + val transport = resultMsg.obj as WebView.WebViewTransport + transport.webView = newWebView + resultMsg.sendToTarget() + return true + } +} \ No newline at end of file diff --git a/app-mobile/src/main/java/ru/radiationx/anilibria/ui/fragments/comments/webview/VkWebViewClient.kt b/app-mobile/src/main/java/ru/radiationx/anilibria/ui/fragments/comments/webview/VkWebViewClient.kt new file mode 100644 index 000000000..397f46fa0 --- /dev/null +++ b/app-mobile/src/main/java/ru/radiationx/anilibria/ui/fragments/comments/webview/VkWebViewClient.kt @@ -0,0 +1,156 @@ +package ru.radiationx.anilibria.ui.fragments.comments.webview + +import android.graphics.Bitmap +import android.net.http.SslError +import android.os.Build +import android.webkit.* +import kotlinx.coroutines.runBlocking +import ru.radiationx.anilibria.apptheme.AppThemeController +import ru.radiationx.anilibria.extension.isDark +import ru.radiationx.anilibria.ui.fragments.comments.VkCommentsCss +import ru.radiationx.anilibria.ui.fragments.comments.VkCommentsViewModel +import ru.radiationx.data.datasource.remote.IClient +import ru.radiationx.shared.ktx.android.toException +import ru.radiationx.shared_app.common.SystemUtils +import timber.log.Timber +import java.io.ByteArrayInputStream +import java.nio.charset.StandardCharsets + +class VkWebViewClient( + private val viewModel: VkCommentsViewModel, + private val systemUtils: SystemUtils, + private val networkClient: IClient, + private val commentsCss: VkCommentsCss, + private val appThemeController: AppThemeController, +) : WebViewClient() { + + var loadingFinished = true + var redirect = false + var authCheckIntercepted = false + + private val authRequestRegex = Regex("oauth\\.vk\\.com\\/authorize\\?|vk\\.com\\/login\\?") + private val authCheckRegex = Regex("vk\\.com\\/login\\?act=authcheck") + private val commentsRegex = Regex("widget_comments(?:\\.\\w+?)?\\.css") + + @Suppress("DEPRECATION", "OverridingDeprecatedMember") + override fun shouldInterceptRequest(view: WebView?, url: String?): WebResourceResponse? { + return tryInterceptAuthCheck(view, url) + ?: tryInterceptComments(view, url) + ?: super.shouldInterceptRequest(view, url) + } + + private fun tryInterceptAuthCheck(view: WebView?, url: String?): WebResourceResponse? { + val needAuth = authCheckRegex.containsMatchIn(url.orEmpty()) + if (needAuth && !authCheckIntercepted) { + authCheckIntercepted = true + val mobileUrl = if (!url.orEmpty().contains("/m.vk.com/")) { + url.orEmpty().replace("vk.com", "/m.vk.com/") + } else { + url.orEmpty() + } + viewModel.authRequest(mobileUrl) + } + return null + } + + private fun tryInterceptComments(view: WebView?, url: String?): WebResourceResponse? { + val needIntercept = commentsRegex.containsMatchIn(url.orEmpty()) + return if (needIntercept) { + val cssSrc = try { + runBlocking { networkClient.get(url.orEmpty(), emptyMap()) } + } catch (ex: Throwable) { + Timber.e(ex) + return WebResourceResponse( + "text/css", + "utf-8", + ByteArrayInputStream( + ex.message.orEmpty().toByteArray(StandardCharsets.UTF_8) + ) + ) + } + var newCss = cssSrc + + val fixCss = if (appThemeController.getTheme().isDark()) { + commentsCss.dark + } else { + commentsCss.light + } + + newCss += fixCss + + val newData = ByteArrayInputStream(newCss.toByteArray(StandardCharsets.UTF_8)) + WebResourceResponse("text/css", "utf-8", newData) + } else { + null + } + } + + @Suppress("OverridingDeprecatedMember") + override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean { + val cookies = CookieManager.getInstance().getCookie(url) + if (!loadingFinished) { + redirect = true + } + + loadingFinished = false + + if (url.orEmpty().contains(authRequestRegex)) { + viewModel.authRequest(url.orEmpty()) + return true + } + systemUtils.externalLink(url.orEmpty()) + return true + } + + override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { + super.onPageStarted(view, url, favicon) + authCheckIntercepted = false + loadingFinished = false + } + + override fun onPageFinished(view: WebView?, url: String?) { + super.onPageFinished(view, url) + + if (!redirect) { + loadingFinished = true + } + + if (loadingFinished && !redirect) { + //progressBar.visibility = View.GONE + viewModel.onPageLoaded() + } else { + redirect = false + } + } + + override fun onReceivedSslError( + view: WebView?, + handler: SslErrorHandler?, + error: SslError?, + ) { + super.onReceivedSslError(view, handler, error) + viewModel.onPageCommitError(error.toException()) + } + + override fun onReceivedHttpError( + view: WebView?, + request: WebResourceRequest?, + errorResponse: WebResourceResponse?, + ) { + super.onReceivedHttpError(view, request, errorResponse) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && view?.url == request?.url?.toString()) { + viewModel.onPageCommitError(errorResponse.toException(request)) + } + } + + override fun onReceivedError( + view: WebView?, + request: WebResourceRequest?, + error: WebResourceError?, + ) { + super.onReceivedError(view, request, error) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && view?.url == request?.url?.toString()) { + viewModel.onPageCommitError(error.toException(request)) + } + } +} \ No newline at end of file From c05e343ff88ebb8f110ba5d529967bcb469a3d3e Mon Sep 17 00:00:00 2001 From: RadiationX Date: Wed, 28 Dec 2022 22:39:37 +0500 Subject: [PATCH 3/3] fix: fast search --- .../anilibria/ui/adapters/search/SuggestionDelegate.kt | 7 ++++--- .../main/kotlin/com/lapism/search/internal/SearchLayout.kt | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app-mobile/src/main/java/ru/radiationx/anilibria/ui/adapters/search/SuggestionDelegate.kt b/app-mobile/src/main/java/ru/radiationx/anilibria/ui/adapters/search/SuggestionDelegate.kt index 5e33f2bb2..282e04b1d 100644 --- a/app-mobile/src/main/java/ru/radiationx/anilibria/ui/adapters/search/SuggestionDelegate.kt +++ b/app-mobile/src/main/java/ru/radiationx/anilibria/ui/adapters/search/SuggestionDelegate.kt @@ -21,7 +21,7 @@ import ru.radiationx.shared_app.imageloader.showImageUrl * Created by radiationx on 13.01.18. */ class SuggestionDelegate( - private val clickListener: (SuggestionItemState) -> Unit + private val clickListener: (SuggestionItemState) -> Unit, ) : AppAdapterDelegate( R.layout.item_fast_search, { it is SuggestionListItem }, @@ -32,7 +32,7 @@ class SuggestionDelegate( class ViewHolder( itemView: View, - private val clickListener: (SuggestionItemState) -> Unit + private val clickListener: (SuggestionItemState) -> Unit, ) : RecyclerView.ViewHolder(itemView) { private val binding by viewBinding() @@ -55,7 +55,8 @@ class SuggestionDelegate( state.matchRanges.filterNot { it.isEmpty() }.forEach { val span = StyleSpan(Typeface.BOLD) val flags = Spannable.SPAN_EXCLUSIVE_EXCLUSIVE - spannableTitle.setSpan(span, it.first, it.last, flags) + val endIndex = (it.last + 1).coerceAtMost(state.title.length) + spannableTitle.setSpan(span, it.first, endIndex, flags) } binding.itemTitle.setText(spannableTitle, TextView.BufferType.SPANNABLE) } diff --git a/lapism-search/src/main/kotlin/com/lapism/search/internal/SearchLayout.kt b/lapism-search/src/main/kotlin/com/lapism/search/internal/SearchLayout.kt index c8b0fb574..673a94dec 100644 --- a/lapism-search/src/main/kotlin/com/lapism/search/internal/SearchLayout.kt +++ b/lapism-search/src/main/kotlin/com/lapism/search/internal/SearchLayout.kt @@ -238,7 +238,7 @@ abstract class SearchLayout @JvmOverloads constructor( mRecyclerView?.layoutManager = LinearLayoutManager(context) mRecyclerView?.visibility = View.GONE mRecyclerView?.isNestedScrollingEnabled = false - mRecyclerView?.itemAnimator = DefaultItemAnimator() + mRecyclerView?.itemAnimator = null mRecyclerView?.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState)