mirror of
https://github.com/gedoor/legado.git
synced 2024-07-06 23:47:49 +08:00
优化
This commit is contained in:
parent
5324dfcec3
commit
d1064c5b4c
@ -144,6 +144,7 @@ object PreferKey {
|
|||||||
const val volumeKeyPage = "volumeKeyPage"
|
const val volumeKeyPage = "volumeKeyPage"
|
||||||
const val volumeKeyPageOnPlay = "volumeKeyPageOnPlay"
|
const val volumeKeyPageOnPlay = "volumeKeyPageOnPlay"
|
||||||
const val mouseWheelPage = "mouseWheelPage"
|
const val mouseWheelPage = "mouseWheelPage"
|
||||||
|
const val recordHeapDump = "recordHeapDump"
|
||||||
|
|
||||||
const val cPrimary = "colorPrimary"
|
const val cPrimary = "colorPrimary"
|
||||||
const val cAccent = "colorAccent"
|
const val cAccent = "colorAccent"
|
||||||
|
@ -4,6 +4,7 @@ import android.annotation.SuppressLint
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.os.Debug
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.webkit.WebSettings
|
import android.webkit.WebSettings
|
||||||
import io.legado.app.constant.AppConst
|
import io.legado.app.constant.AppConst
|
||||||
@ -12,13 +13,24 @@ import io.legado.app.exception.NoStackTraceException
|
|||||||
import io.legado.app.help.config.AppConfig
|
import io.legado.app.help.config.AppConfig
|
||||||
import io.legado.app.help.config.LocalConfig
|
import io.legado.app.help.config.LocalConfig
|
||||||
import io.legado.app.model.ReadAloud
|
import io.legado.app.model.ReadAloud
|
||||||
import io.legado.app.utils.*
|
import io.legado.app.utils.FileDoc
|
||||||
|
import io.legado.app.utils.FileUtils
|
||||||
|
import io.legado.app.utils.createFileIfNotExist
|
||||||
|
import io.legado.app.utils.createFolderReplace
|
||||||
|
import io.legado.app.utils.externalCache
|
||||||
|
import io.legado.app.utils.getFile
|
||||||
|
import io.legado.app.utils.longToastOnUiLegacy
|
||||||
|
import io.legado.app.utils.stackTraceStr
|
||||||
|
import io.legado.app.utils.writeText
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
import java.io.StringWriter
|
import java.io.StringWriter
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.Date
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlin.collections.component1
|
||||||
|
import kotlin.collections.component2
|
||||||
|
import kotlin.collections.set
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 异常管理类
|
* 异常管理类
|
||||||
@ -69,6 +81,9 @@ class CrashHandler(val context: Context) : Thread.UncaughtExceptionHandler {
|
|||||||
LocalConfig.appCrash = true
|
LocalConfig.appCrash = true
|
||||||
//保存日志文件
|
//保存日志文件
|
||||||
saveCrashInfo2File(ex)
|
saveCrashInfo2File(ex)
|
||||||
|
if (ex is OutOfMemoryError && AppConfig.recordHeapDump) {
|
||||||
|
doHeapDump()
|
||||||
|
}
|
||||||
context.longToastOnUiLegacy(ex.stackTraceStr)
|
context.longToastOnUiLegacy(ex.stackTraceStr)
|
||||||
Thread.sleep(3000)
|
Thread.sleep(3000)
|
||||||
}
|
}
|
||||||
@ -148,6 +163,19 @@ class CrashHandler(val context: Context) : Thread.UncaughtExceptionHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进行堆转储
|
||||||
|
*/
|
||||||
|
fun doHeapDump() {
|
||||||
|
val heapDir = appCtx
|
||||||
|
.externalCache
|
||||||
|
.getFile("heapDump")
|
||||||
|
heapDir.createFolderReplace()
|
||||||
|
val heapFile = heapDir.getFile("heap-dump-${System.currentTimeMillis()}.hprof")
|
||||||
|
val heapDumpName = heapFile.absolutePath
|
||||||
|
Debug.dumpHprofData(heapDumpName)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -449,6 +449,8 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
|
|||||||
|
|
||||||
val recordLog get() = appCtx.getPrefBoolean(PreferKey.recordLog)
|
val recordLog get() = appCtx.getPrefBoolean(PreferKey.recordLog)
|
||||||
|
|
||||||
|
val recordHeapDump get() = appCtx.getPrefBoolean(PreferKey.recordHeapDump, false)
|
||||||
|
|
||||||
val loadCoverOnlyWifi get() = appCtx.getPrefBoolean(PreferKey.loadCoverOnlyWifi, false)
|
val loadCoverOnlyWifi get() = appCtx.getPrefBoolean(PreferKey.loadCoverOnlyWifi, false)
|
||||||
|
|
||||||
val showAddToShelfAlert get() = appCtx.getPrefBoolean(PreferKey.showAddToShelfAlert, true)
|
val showAddToShelfAlert get() = appCtx.getPrefBoolean(PreferKey.showAddToShelfAlert, true)
|
||||||
|
@ -12,6 +12,7 @@ import io.legado.app.R
|
|||||||
import io.legado.app.constant.AppConst.appInfo
|
import io.legado.app.constant.AppConst.appInfo
|
||||||
import io.legado.app.constant.AppLog
|
import io.legado.app.constant.AppLog
|
||||||
import io.legado.app.help.AppUpdate
|
import io.legado.app.help.AppUpdate
|
||||||
|
import io.legado.app.help.CrashHandler
|
||||||
import io.legado.app.help.config.AppConfig
|
import io.legado.app.help.config.AppConfig
|
||||||
import io.legado.app.help.coroutine.Coroutine
|
import io.legado.app.help.coroutine.Coroutine
|
||||||
import io.legado.app.ui.widget.dialog.TextDialog
|
import io.legado.app.ui.widget.dialog.TextDialog
|
||||||
@ -61,6 +62,7 @@ class AboutFragment : PreferenceFragmentCompat() {
|
|||||||
"gzGzh" -> requireContext().sendToClip(getString(R.string.legado_gzh))
|
"gzGzh" -> requireContext().sendToClip(getString(R.string.legado_gzh))
|
||||||
"crashLog" -> showDialogFragment<CrashLogsDialog>()
|
"crashLog" -> showDialogFragment<CrashLogsDialog>()
|
||||||
"saveLog" -> saveLog()
|
"saveLog" -> saveLog()
|
||||||
|
"saveHeapDump" -> saveHeapDump()
|
||||||
}
|
}
|
||||||
return super.onPreferenceTreeClick(preference)
|
return super.onPreferenceTreeClick(preference)
|
||||||
}
|
}
|
||||||
@ -137,10 +139,50 @@ class AboutFragment : PreferenceFragmentCompat() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val heapFile = FileDoc.fromFile(File(appCtx.externalCacheDir, "heapDump")).list()
|
||||||
|
?.firstOrNull()
|
||||||
|
if (heapFile != null) {
|
||||||
|
doc.find("heapDump")?.delete()
|
||||||
|
val heapDumpDoc = doc.createFolderIfNotExist("heapDump")
|
||||||
|
heapFile.openInputStream().getOrNull()?.use { input ->
|
||||||
|
heapDumpDoc.createFileIfNotExist(heapFile.name).openOutputStream().getOrNull()
|
||||||
|
?.use {
|
||||||
|
input.copyTo(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
toastOnUi("已保存至备份目录")
|
toastOnUi("已保存至备份目录")
|
||||||
}.onError {
|
}.onError {
|
||||||
AppLog.put("保存日志出错\n${it.localizedMessage}", it, true)
|
AppLog.put("保存日志出错\n${it.localizedMessage}", it, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun saveHeapDump() {
|
||||||
|
Coroutine.async {
|
||||||
|
val backupPath = AppConfig.backupPath ?: let {
|
||||||
|
toastOnUi("未设置备份目录")
|
||||||
|
return@async
|
||||||
|
}
|
||||||
|
toastOnUi("开始保存堆转储")
|
||||||
|
CrashHandler.doHeapDump()
|
||||||
|
val heapFile = FileDoc.fromFile(File(appCtx.externalCacheDir, "heapDump")).list()
|
||||||
|
?.firstOrNull() ?: let {
|
||||||
|
toastOnUi("未找到堆转储文件")
|
||||||
|
return@async
|
||||||
|
}
|
||||||
|
val doc = FileDoc.fromUri(Uri.parse(backupPath), true)
|
||||||
|
doc.find("heapDump")?.delete()
|
||||||
|
val heapDumpDoc = doc.createFolderIfNotExist("heapDump")
|
||||||
|
heapFile.openInputStream().getOrNull()?.use { input ->
|
||||||
|
heapDumpDoc.createFileIfNotExist(heapFile.name).openOutputStream().getOrNull()
|
||||||
|
?.use {
|
||||||
|
input.copyTo(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toastOnUi("已保存至备份目录")
|
||||||
|
}.onError {
|
||||||
|
AppLog.put("保存堆转储失败\n${it.localizedMessage}", it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -2,12 +2,19 @@ package io.legado.app.ui.login
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
|
import android.net.Uri
|
||||||
import android.net.http.SslError
|
import android.net.http.SslError
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.webkit.*
|
import android.webkit.CookieManager
|
||||||
|
import android.webkit.SslErrorHandler
|
||||||
|
import android.webkit.WebChromeClient
|
||||||
|
import android.webkit.WebResourceRequest
|
||||||
|
import android.webkit.WebSettings
|
||||||
|
import android.webkit.WebView
|
||||||
|
import android.webkit.WebViewClient
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.base.BaseFragment
|
import io.legado.app.base.BaseFragment
|
||||||
@ -16,8 +23,10 @@ import io.legado.app.data.entities.BaseSource
|
|||||||
import io.legado.app.databinding.FragmentWebViewLoginBinding
|
import io.legado.app.databinding.FragmentWebViewLoginBinding
|
||||||
import io.legado.app.help.http.CookieStore
|
import io.legado.app.help.http.CookieStore
|
||||||
import io.legado.app.lib.theme.accentColor
|
import io.legado.app.lib.theme.accentColor
|
||||||
import io.legado.app.utils.gone
|
|
||||||
import io.legado.app.utils.NetworkUtils
|
import io.legado.app.utils.NetworkUtils
|
||||||
|
import io.legado.app.utils.gone
|
||||||
|
import io.legado.app.utils.longSnackbar
|
||||||
|
import io.legado.app.utils.openUrl
|
||||||
import io.legado.app.utils.snackbar
|
import io.legado.app.utils.snackbar
|
||||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
||||||
|
|
||||||
@ -89,6 +98,33 @@ class WebViewLoginFragment : BaseFragment(R.layout.fragment_web_view_login) {
|
|||||||
super.onPageFinished(view, url)
|
super.onPageFinished(view, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun shouldOverrideUrlLoading(
|
||||||
|
view: WebView,
|
||||||
|
request: WebResourceRequest
|
||||||
|
): Boolean {
|
||||||
|
return shouldOverrideUrlLoading(request.url)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION", "OVERRIDE_DEPRECATION", "KotlinRedundantDiagnosticSuppress")
|
||||||
|
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
|
||||||
|
return shouldOverrideUrlLoading(Uri.parse(url))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun shouldOverrideUrlLoading(url: Uri): Boolean {
|
||||||
|
when (url.scheme) {
|
||||||
|
"http", "https" -> {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
binding.root.longSnackbar(R.string.jump_to_another_app, R.string.confirm) {
|
||||||
|
context?.openUrl(url)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("WebViewClientOnReceivedSslError")
|
@SuppressLint("WebViewClientOnReceivedSslError")
|
||||||
override fun onReceivedSslError(
|
override fun onReceivedSslError(
|
||||||
view: WebView?,
|
view: WebView?,
|
||||||
|
@ -1141,4 +1141,7 @@
|
|||||||
<string name="clear_webview_data_success">清除成功,3秒后自动重启应用</string>
|
<string name="clear_webview_data_success">清除成功,3秒后自动重启应用</string>
|
||||||
<string name="key_page_on_long_press">按键长按翻页</string>
|
<string name="key_page_on_long_press">按键长按翻页</string>
|
||||||
<string name="save_log">保存日志</string>
|
<string name="save_log">保存日志</string>
|
||||||
|
<string name="save_heap_dump">保存堆转储</string>
|
||||||
|
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
|
||||||
|
<string name="record_heap_dump_t">记录堆转储</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1144,4 +1144,7 @@
|
|||||||
<string name="clear_webview_data_success">清除成功,3秒后自动重启应用</string>
|
<string name="clear_webview_data_success">清除成功,3秒后自动重启应用</string>
|
||||||
<string name="key_page_on_long_press">按键长按翻页</string>
|
<string name="key_page_on_long_press">按键长按翻页</string>
|
||||||
<string name="save_log">保存日志</string>
|
<string name="save_log">保存日志</string>
|
||||||
|
<string name="save_heap_dump">保存堆转储</string>
|
||||||
|
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
|
||||||
|
<string name="record_heap_dump_t">记录堆转储</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1144,4 +1144,7 @@
|
|||||||
<string name="clear_webview_data_success">清除成功,3秒后自动重启应用</string>
|
<string name="clear_webview_data_success">清除成功,3秒后自动重启应用</string>
|
||||||
<string name="key_page_on_long_press">按键长按翻页</string>
|
<string name="key_page_on_long_press">按键长按翻页</string>
|
||||||
<string name="save_log">保存日志</string>
|
<string name="save_log">保存日志</string>
|
||||||
|
<string name="save_heap_dump">保存堆转储</string>
|
||||||
|
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
|
||||||
|
<string name="record_heap_dump_t">记录堆转储</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1140,4 +1140,7 @@ Còn </string>
|
|||||||
<string name="clear_webview_data_success">清除成功,3秒后自动重启应用</string>
|
<string name="clear_webview_data_success">清除成功,3秒后自动重启应用</string>
|
||||||
<string name="key_page_on_long_press">按键长按翻页</string>
|
<string name="key_page_on_long_press">按键长按翻页</string>
|
||||||
<string name="save_log">保存日志</string>
|
<string name="save_log">保存日志</string>
|
||||||
|
<string name="save_heap_dump">保存堆转储</string>
|
||||||
|
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
|
||||||
|
<string name="record_heap_dump_t">记录堆转储</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1141,4 +1141,7 @@
|
|||||||
<string name="clear_webview_data_success">清除成功,3秒后自动重启应用</string>
|
<string name="clear_webview_data_success">清除成功,3秒后自动重启应用</string>
|
||||||
<string name="key_page_on_long_press">按键长按翻页</string>
|
<string name="key_page_on_long_press">按键长按翻页</string>
|
||||||
<string name="save_log">保存日志</string>
|
<string name="save_log">保存日志</string>
|
||||||
|
<string name="save_heap_dump">保存堆转储</string>
|
||||||
|
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
|
||||||
|
<string name="record_heap_dump_t">记录堆转储</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1143,4 +1143,7 @@
|
|||||||
<string name="clear_webview_data_success">清除成功,3秒后自动重启应用</string>
|
<string name="clear_webview_data_success">清除成功,3秒后自动重启应用</string>
|
||||||
<string name="key_page_on_long_press">按键长按翻页</string>
|
<string name="key_page_on_long_press">按键长按翻页</string>
|
||||||
<string name="save_log">保存日志</string>
|
<string name="save_log">保存日志</string>
|
||||||
|
<string name="save_heap_dump">保存堆转储</string>
|
||||||
|
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
|
||||||
|
<string name="record_heap_dump_t">记录堆转储</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1143,4 +1143,7 @@
|
|||||||
<string name="clear_webview_data_success">清除成功,3秒后自动重启应用</string>
|
<string name="clear_webview_data_success">清除成功,3秒后自动重启应用</string>
|
||||||
<string name="key_page_on_long_press">按键长按翻页</string>
|
<string name="key_page_on_long_press">按键长按翻页</string>
|
||||||
<string name="save_log">保存日志</string>
|
<string name="save_log">保存日志</string>
|
||||||
|
<string name="save_heap_dump">保存堆转储</string>
|
||||||
|
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
|
||||||
|
<string name="record_heap_dump_t">记录堆转储</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1144,4 +1144,7 @@
|
|||||||
<string name="clear_webview_data_success">Cleared successfully, automatically restarts the application after 3 seconds</string>
|
<string name="clear_webview_data_success">Cleared successfully, automatically restarts the application after 3 seconds</string>
|
||||||
<string name="key_page_on_long_press">Press and hold the key to turn the page</string>
|
<string name="key_page_on_long_press">Press and hold the key to turn the page</string>
|
||||||
<string name="save_log">保存日志</string>
|
<string name="save_log">保存日志</string>
|
||||||
|
<string name="save_heap_dump">保存堆转储</string>
|
||||||
|
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
|
||||||
|
<string name="record_heap_dump_t">记录堆转储</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -39,6 +39,11 @@
|
|||||||
android:title="@string/save_log"
|
android:title="@string/save_log"
|
||||||
app:iconSpaceReserved="false" />
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
|
<io.legado.app.lib.prefs.Preference
|
||||||
|
android:key="saveHeapDump"
|
||||||
|
android:title="@string/save_heap_dump"
|
||||||
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
<io.legado.app.lib.prefs.Preference
|
<io.legado.app.lib.prefs.Preference
|
||||||
android:key="privacyPolicy"
|
android:key="privacyPolicy"
|
||||||
android:title="@string/privacy_policy"
|
android:title="@string/privacy_policy"
|
||||||
|
@ -188,6 +188,13 @@
|
|||||||
android:title="@string/record_log"
|
android:title="@string/record_log"
|
||||||
app:iconSpaceReserved="false" />
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
|
<io.legado.app.lib.prefs.SwitchPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="recordHeapDump"
|
||||||
|
android:summary="@string/record_heap_dump_s"
|
||||||
|
android:title="@string/record_heap_dump_t"
|
||||||
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
</io.legado.app.lib.prefs.PreferenceCategory>
|
</io.legado.app.lib.prefs.PreferenceCategory>
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.script
|
package com.script
|
||||||
|
|
||||||
|
import org.mozilla.javascript.Scriptable
|
||||||
|
|
||||||
abstract class CompiledScript {
|
abstract class CompiledScript {
|
||||||
|
|
||||||
abstract fun getEngine(): ScriptEngine
|
abstract fun getEngine(): ScriptEngine
|
||||||
@ -10,6 +12,9 @@ abstract class CompiledScript {
|
|||||||
@Throws(ScriptException::class)
|
@Throws(ScriptException::class)
|
||||||
abstract fun eval(context: ScriptContext): Any?
|
abstract fun eval(context: ScriptContext): Any?
|
||||||
|
|
||||||
|
@Throws(ScriptException::class)
|
||||||
|
abstract fun eval(scope: Scriptable): Any?
|
||||||
|
|
||||||
@Throws(ScriptException::class)
|
@Throws(ScriptException::class)
|
||||||
fun eval(bindings: Bindings?): Any? {
|
fun eval(bindings: Bindings?): Any? {
|
||||||
var ctxt = getEngine().context
|
var ctxt = getEngine().context
|
||||||
|
@ -69,4 +69,26 @@ internal class RhinoCompiledScript(
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun eval(scope: Scriptable): Any? {
|
||||||
|
val cx = Context.enter()
|
||||||
|
val result: Any?
|
||||||
|
try {
|
||||||
|
val ret = script.exec(cx, scope)
|
||||||
|
result = engine.unwrapReturnValue(ret)
|
||||||
|
} catch (re: RhinoException) {
|
||||||
|
val line = if (re.lineNumber() == 0) -1 else re.lineNumber()
|
||||||
|
val msg: String = if (re is JavaScriptException) {
|
||||||
|
re.value.toString()
|
||||||
|
} else {
|
||||||
|
re.toString()
|
||||||
|
}
|
||||||
|
val se = ScriptException(msg, re.sourceName(), line)
|
||||||
|
se.initCause(re)
|
||||||
|
throw se
|
||||||
|
} finally {
|
||||||
|
Context.exit()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user